Compare commits

..

1 Commits

Author SHA1 Message Date
Eric Fennis
251bfab76a Remove UMD support 2025-08-01 12:47:22 +02:00
513 changed files with 6254 additions and 9088 deletions

View File

@@ -7,10 +7,8 @@ body:
value: | value: |
Before submitting an icon request check if it has already been requested. If there is an open request, please add a 👍. Before submitting an icon request check if it has already been requested. If there is an open request, please add a 👍.
> [!CAUTION] **Important note**: No new brand logos are allowed, see https://github.com/lucide-icons/lucide/issues/670.
> New brand logos are **not** allowed, see our official statement: https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md. Existing brand icons will also be phased out. For brand icons, consider using https://simpleicons.org, which offers purpose-built SVGs that are also on a 24×24px grid.
>
> Existing brand icons are being phased out. Consider using https://simpleicons.org, which offers purpose-built SVGs that are also on a 24×24px grid.
- type: input - type: input
id: name id: name
attributes: attributes:
@@ -43,9 +41,9 @@ body:
required: true required: true
- label: I have searched existing icons to make sure it does not already exist and I didn't find any. - label: I have searched existing icons to make sure it does not already exist and I didn't find any.
required: true required: true
- label: I am not requesting a brand logo and the art is not protected by copyright, see more at https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md - label: I am not requesting a brand logo and the art is not protected by copyright.
required: true required: true
- label: I am not requesting an icon that includes religious, war/violence related, political imagery or hate symbols. - label: I am not requesting an icon that includes religious, political imagery or hate symbols.
required: true required: true
- label: I have provided appropriate use cases for the icon(s) requested. - label: I have provided appropriate use cases for the icon(s) requested.
required: true required: true

View File

@@ -25,12 +25,10 @@ Common scopes: icons, docs, studio, site, dev
### Concept <!-- ONLY for new icons --> ### Concept <!-- ONLY for new icons -->
<!-- All of these requirements must be fulfilled. --> <!-- All of these requirements must be fulfilled. -->
<!-- IMPORTANT! Please read our official statement on brand logos in Lucide: -->
<!-- https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md -->
- [ ] I have provided valid use cases for each icon. - [ ] I have provided valid use cases for each icon.
- [ ] I have [not added any brand or logo icon](https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md). - [ ] I have not added any a brand or logo icon.
- [ ] I have not used any hate symbols. - [ ] I have not used any hate symbols.
- [ ] I have not included any religious, war/violence related or political imagery. - [ ] I have not included any religious or political imagery.
### Author, credits & license<!-- ONLY for new icons. --> ### Author, credits & license<!-- ONLY for new icons. -->
<!-- Please choose one of the following, and put an "x" next to it. --> <!-- Please choose one of the following, and put an "x" next to it. -->

View File

@@ -7,10 +7,6 @@ on:
paths: paths:
- icons/**/*.svg - icons/**/*.svg
permissions:
id-token: write # Required for OIDC
contents: write
jobs: jobs:
create-release: create-release:
if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)') if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)')
@@ -19,9 +15,9 @@ jobs:
VERSION: ${{ steps.new-version.outputs.NEW_VERSION }} VERSION: ${{ steps.new-version.outputs.NEW_VERSION }}
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6 - uses: actions/setup-node@v4
with: with:
cache: 'pnpm' cache: 'pnpm'
node-version-file: 'package.json' node-version-file: 'package.json'
@@ -36,19 +32,25 @@ jobs:
id: latest-tag id: latest-tag
run: echo "LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_OUTPUT 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 - name: Check if we can patch
run: pnpm semver $LATEST_TAG -i minor run: .github/workflows/version-up.sh --minor
env:
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
- name: Create new version - name: Create new version
id: new-version id: new-version
run: echo "NEW_VERSION=$(pnpm semver $LATEST_TAG -i minor)" >> $GITHUB_OUTPUT 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
env: env:
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }} GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
- name: Check output - name: Check output
run: | run: |
@@ -62,6 +64,38 @@ jobs:
name: Version ${{ steps.new-version.outputs.NEW_VERSION }} name: Version ${{ steps.new-version.outputs.NEW_VERSION }}
generate_release_notes: true 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: start-release:
if: github.repository == 'lucide-icons/lucide' if: github.repository == 'lucide-icons/lucide'
needs: create-release needs: create-release

View File

@@ -1,4 +1,4 @@
name: Close Icon Requests with Brand Terms name: Close Issue with Banned Phrases
on: on:
issues: issues:
@@ -11,38 +11,25 @@ jobs:
issues: write issues: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v4
- name: Load stopwords from JSON & check issue title & body - name: Check for blocked phrases in issue title
if: contains(github.event.issue.labels.*.name, '🙌 icon request')
run: | run: |
ISSUE_TITLE=$(jq -r '.issue.title' "$GITHUB_EVENT_PATH") ISSUE_TITLE=$(jq -r '.issue.title' "$GITHUB_EVENT_PATH")
ISSUE_BODY=$(jq -r '.issue.body // ""' "$GITHUB_EVENT_PATH") BLOCKED_PHRASES=("twitter" "whatsapp" "logo" "google" "tiktok" "facebook" "slack" "discord" "bluesky" "spotify" "behance" "pix" "x.com" "telegram")
ICON_NAME_SECTION=$(printf '%s\n' "$ISSUE_BODY" | awk '/### Icon name/{flag=1; next} /^### /{flag=0} flag')
jq -r 'to_entries[] | "\(.key) \(.value)"' brand-stopwords.json | while read -r KEY VALUE; do # Check title and body for blocked phrases
SAFE_KEY=$(printf '%s\n' "$KEY" | sed 's/[][\.^$*]/\\&/g') for PHRASE in "${BLOCKED_PHRASES[@]}"
SAFE_VALUE=$(printf '%s\n' "$VALUE" | sed 's/[][\.^$*]/\\&/g') 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.
if echo "$ISSUE_TITLE" | grep -iqE "$SAFE_KEY|$SAFE_VALUE" || \ Read more about brand guideline rules at [We're not accepting new Brand icons #670](https://github.com/lucide-icons/lucide/issues/670).
{ [ -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 gh issue lock ${{ github.event.issue.number }} --reason spam
exit 0 exit 1
fi fi
done done
env:
env: GH_TOKEN: ${{ github.token }}
GH_TOKEN: ${{ github.token }}

View File

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

View File

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

View File

@@ -13,8 +13,8 @@ jobs:
permissions: permissions:
contents: read contents: read
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: actions/setup-node@v6 - uses: actions/setup-node@v4
with: with:
node-version-file: 'package.json' node-version-file: 'package.json'
- name: Get changed files - name: Get changed files
@@ -34,6 +34,6 @@ jobs:
permissions: permissions:
contents: read contents: read
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- name: Check Uniqueness of Aliases - 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 '" 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: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6 - uses: actions/setup-node@v4
with: with:
cache: 'pnpm' cache: 'pnpm'
node-version-file: 'package.json' node-version-file: 'package.json'
@@ -27,9 +27,9 @@ jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6 - uses: actions/setup-node@v4
with: with:
cache: 'pnpm' cache: 'pnpm'
node-version-file: 'package.json' node-version-file: 'package.json'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,13 +18,9 @@ on:
description: Version description: Version
required: true required: true
permissions:
id-token: write # Required for OIDC
contents: write
jobs: jobs:
pre-release: pre-release:
if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere", "jguddas"]', github.actor) if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere"]', github.actor)
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
VERSION: ${{ steps.get_version.outputs.VERSION }} VERSION: ${{ steps.get_version.outputs.VERSION }}
@@ -43,8 +39,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: pre-release needs: pre-release
permissions: permissions:
id-token: write # Required for OIDC id-token: write
contents: read
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -53,6 +48,7 @@ jobs:
'lucide', 'lucide',
'lucide-react', 'lucide-react',
'lucide-react-native', 'lucide-react-native',
'lucide-vue',
'lucide-vue-next', 'lucide-vue-next',
'lucide-angular', 'lucide-angular',
'lucide-preact', 'lucide-preact',
@@ -62,9 +58,9 @@ jobs:
'@lucide/svelte', '@lucide/svelte',
] ]
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6 - uses: actions/setup-node@v4
with: with:
cache: 'pnpm' cache: 'pnpm'
node-version-file: 'package.json' node-version-file: 'package.json'
@@ -72,6 +68,9 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile 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 - name: Set new version
run: pnpm --filter ${{ matrix.package }} version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version run: pnpm --filter ${{ matrix.package }} version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
@@ -91,14 +90,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [pre-release, lucide-font] needs: [pre-release, lucide-font]
permissions: permissions:
id-token: write # Required for OIDC id-token: write
contents: read
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6 - uses: actions/setup-node@v4
with: with:
cache: 'pnpm' cache: 'pnpm'
node-version-file: 'package.json' node-version-file: 'package.json'
@@ -106,6 +103,9 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile run: pnpm install --frozen-lockfile
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- name: Set new version - name: Set new version
run: pnpm --filter lucide-static version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version run: pnpm --filter lucide-static version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
@@ -125,9 +125,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: pre-release needs: pre-release
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6 - uses: actions/setup-node@v4
with: with:
cache: 'pnpm' cache: 'pnpm'
node-version-file: 'package.json' node-version-file: 'package.json'
@@ -151,11 +151,9 @@ jobs:
if: github.repository == 'lucide-icons/lucide' if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [pre-release, lucide-font] needs: [pre-release, lucide-font]
permissions:
id-token: write # Required for OIDC
contents: write
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
- name: Zip font and icons - name: Zip font and icons
run: | run: |

View File

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

284
.github/workflows/version-up.sh vendored Executable file
View File

@@ -0,0 +1,284 @@
#!/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/categoriesData.json
docs/.vitepress/data/iconDetails docs/.vitepress/data/iconDetails
docs/.vitepress/data/relatedIcons.json docs/.vitepress/data/relatedIcons.json
docs/.vitepress/data/brandStopwords.json
docs/.vercel docs/.vercel
docs/.nitro docs/.nitro
.gitignore .gitignore

1
.npmrc Normal file
View File

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

View File

@@ -1,5 +1,4 @@
{ {
"$schema": "https://raw.githubusercontent.com/Yash-Singh1/vscode-snippets-json-schema/main/schema.json",
"Lucide SVG": { "Lucide SVG": {
"scope": "xml", "scope": "xml",
"description": "Base SVG with Lucide attributes.", "description": "Base SVG with Lucide attributes.",
@@ -52,16 +51,6 @@
], ],
"body": "<circle cx=\"${2:12}\" cy=\"${3:$2}\" r=\"${1|10,2,.5\" fill=\"currentColor|}\" />" "body": "<circle cx=\"${2:12}\" cy=\"${3:$2}\" r=\"${1|10,2,.5\" fill=\"currentColor|}\" />"
}, },
"Squircle": {
"scope": "xml",
"description": "SVG `path` with Lucide defaults.",
"prefix": [
"squircle",
"path",
"<path"
],
"body": "<path d=\"M12 3c7.2 0 9 1.8 9 9s-1.8 9-9 9-9-1.8-9-9 1.8-9 9-9\" />"
},
"Ellipse": { "Ellipse": {
"scope": "xml", "scope": "xml",
"description": "SVG `ellipse`.", "description": "SVG `ellipse`.",

View File

@@ -1,71 +0,0 @@
# Our Official Stance on Including Brand Logos in Lucide
## TL;DR
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**.
If you need brand logos, we recommend [Simple Icons](https://simpleicons.org/), which provides an extensive, legally safer collection of brand marks.
---
## 1. Historical Context
This is not a new debate — other icon sets have gone through the same discussion:
- **Material Design Icons** [deprecated all brand icons](https://github.com/Templarian/MaterialDesign/issues/6602) because they didn't fit the style, didn't work well in one color, and often looked out of place in a 24×24px grid.
- **Feather Icons** [came to the same conclusion](https://github.com/feathericons/feather/issues/763): brand logos have their own style, and forcing them into another inevitably leads to aesthetic compromises.
- **Lucide** learned from these examples — we'd rather focus on making a consistent set of non-brand icons that all work together.
## 2. Legal Considerations
Most brand logos:
- Are **protected by trademark or copyright**.
- Have **strict rules** for how they can be used (colors, spacing, proportions, etc.).
- **Don't allow modification** — but we'd have to change them to fit Lucide's style.
This means adding them could:
1. Break copyright or trademark law.
2. Make both you and the Lucide project legally responsible.
3. Force us to review every new request one by one for legal issues — something we simply can't do.
> **Note:** Simple Icons avoids this by keeping logos exactly as the brand provides them — though even they sometimes face [legal challenges](https://github.com/simple-icons/simple-icons/issues/11236).
## 3. Design & Consistency
Lucide is all about **visual consistency**.
Adding brand logos would:
- Break [our own design rules](https://lucide.dev/guide/design/icon-design-guide#icon-design-principles) for shapes, proportions, and stroke.
- Mix two fundamentally different categories of graphics (pictograms vs. corporate logos).
- Create a library where a subset of icons will always look "out of place".
If the logos are not in Lucide's style, why include them in Lucide at all? Better to use them from a dedicated brand icon source.
## 4. Maintenance Burden
Even with our current **"no brand icon requests"** policy, people still request them regularly.
Having any brand icons in the set:
- Makes people think we might add more in the future.
- Leads to repeated requests and the same conversations over and over.
- Wastes maintainer time redirecting people to the same explanation.
Removing them entirely solves this problem.
## 5. Recommended Alternatives
If you need brand icons, try:
- [Simple Icons](https://simpleicons.org/): offers a huge range of brands, in consistent SVG format, using a 24×24 viewBox, the same as ours.
- Official brand asset pages: most major companies provide downloadable SVGs.
You can use these alongside Lucide without bloating our core library.
## Final Words
Lucide is an **icon** set, not a **logo** set.
Logos belong in dedicated logo resources.
We're focusing on what Lucide does best: providing a clean, cohesive, and legally safe collection of open-source icons.

26
LICENSE
View File

@@ -1,6 +1,6 @@
ISC License ISC License
Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2023 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2025. Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2022.
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@@ -13,27 +13,3 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
The MIT License (MIT) (for portions derived from Feather)
Copyright (c) 2013-2023 Cole Bemis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -57,12 +57,6 @@ For more info on how to contribute please see the [contribution guidelines](http
Caught a mistake or want to contribute to the documentation? [Edit this page on Github](https://github.com/lucide-icons/lucide/blob/main/README.md) Caught a mistake or want to contribute to the documentation? [Edit this page on Github](https://github.com/lucide-icons/lucide/blob/main/README.md)
## About brand logos
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)
## Community ## Community
Join the community on our [Discord](https://discord.gg/EH6nSts) server! Join the community on our [Discord](https://discord.gg/EH6nSts) server!
@@ -90,5 +84,5 @@ Thank you to all the people who contributed to Lucide!
### Awesome backers 🍺 ### Awesome backers 🍺
<a href="https://www.scipress.io?utm_source=lucide"><img src="docs/public/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
<a href="https://github.com/pdfme/pdfme"><img src="docs/public/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a> <a href="https://github.com/pdfme/pdfme"><img src="docs/public/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
<a href="https://www.fina.money/"><img src="docs/public/sponsors/fina-money.png" width="180" alt="Fina Money sponsor badge" /></a>

View File

@@ -1,149 +0,0 @@
{
"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 +1,5 @@
{ {
"$schema": "../category.schema.json", "$schema": "../category.schema.json",
"title": "Navigation, Maps, and POIs", "title": "Navigation",
"icon": "compass" "icon": "compass"
} }

View File

@@ -15,10 +15,8 @@ export default eventHandler((event) => {
const src = Buffer.from(data, 'base64').toString('utf8').replaceAll('\n', ''); const src = Buffer.from(data, 'base64').toString('utf8').replaceAll('\n', '');
const width = parseInt((src.includes('<svg ') ? src.match(/width="(\d+)"/)?.[1] : null) ?? '24'); const width = parseInt((src.includes('svg') ? src.match(/width="(\d+)"/)?.[1] : null) ?? '24');
const height = parseInt( const height = parseInt((src.includes('svg') ? src.match(/height="(\d+)"/)?.[1] : null) ?? '24');
(src.includes('<svg ') ? src.match(/height="(\d+)"/)?.[1] : null) ?? '24',
);
const children = []; const children = [];

View File

@@ -17,13 +17,6 @@ export default eventHandler((event) => {
.replaceAll('\n', '') .replaceAll('\n', '')
.replace(/<svg[^>]*>|<\/svg>/g, ''); .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 = []; const children = [];
const oldSrc = iconNodes[name] const oldSrc = iconNodes[name]
@@ -34,9 +27,7 @@ export default eventHandler((event) => {
const svg = Buffer.from( const svg = Buffer.from(
// We can't use jsx here, is not supported here by nitro. // We can't use jsx here, is not supported here by nitro.
renderToString( renderToString(createElement(Diff, { oldSrc, newSrc, showGrid: true }, children)),
createElement(Diff, { oldSrc, newSrc, showGrid: true, height, width }, children),
),
).toString('utf8'); ).toString('utf8');
defaultContentType(event, 'image/svg+xml'); defaultContentType(event, 'image/svg+xml');

View File

@@ -1,10 +1,6 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3'; import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
import { Resvg, initWasm } from '@resvg/resvg-wasm'; import { Resvg, initWasm } from '@resvg/resvg-wasm';
import iconNodes from '../../../data/iconNodes';
import wasm from './loadWasm'; import wasm from './loadWasm';
import { createElement } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import createLucideIcon from 'lucide-react/src/createLucideIcon';
var initializedResvg = initWasm(wasm); var initializedResvg = initWasm(wasm);
@@ -13,37 +9,27 @@ export default eventHandler(async (event) => {
await initializedResvg; await initializedResvg;
const imageSize = 96; const imageSize = 96;
const name = params.data.split('/').at(-3); const [iconSizeString, svgData] = params.data.split('/');
const iconSizeString = params.data.split('/').at(-2);
const svgData = params.data.split('/').at(-1);
const iconSize = parseInt(iconSizeString, 10); const iconSize = parseInt(iconSizeString, 10);
const data = svgData.slice(0, -4); const data = svgData.slice(0, -4);
const prevSvg = iconNodes[name]
? renderToStaticMarkup(createElement(createLucideIcon(name, iconNodes[name])))
: undefined;
const src = Buffer.from(data, 'base64').toString('utf8'); const src = Buffer.from(data, 'base64').toString('utf8');
const svg = (src.includes('<svg') ? src : `<svg>${src}</svg>`) const svg = (src.includes('<svg') ? src : `<svg>${src}</svg>`)
.replace(/(\r\n|\n|\r)/gm, '') .replace(/(\r\n|\n|\r)/gm, '')
.replace( .replace(
/<svg[^>]*>/, /<svg[^>]*/,
`<svg `<svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="${iconSize}" width="${iconSize}"
height="${prevSvg ? iconSize * 2 : iconSize}" height="${iconSize}"
viewBox="0 0 24 ${prevSvg ? 48 : 24}" viewBox="0 0 24 24"
fill="none" fill="none"
stroke="#fff" stroke="#fff"
stroke-width="2" stroke-width="2"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
>
${prevSvg?.replaceAll('\n', '').replace(/<svg[^>]*>|<\/svg>/g, '')}
<g transform="translate(0, ${prevSvg ? 24 : 0})">
`, `,
) );
.replace(/<\/svg>/, '</g></svg>');
const resvg = new Resvg(svg, { background: '#000' }); const resvg = new Resvg(svg, { background: '#000' });
const pngData = resvg.render(); const pngData = resvg.render();
@@ -53,7 +39,7 @@ export default eventHandler(async (event) => {
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000'); setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
return ` return `
<svg xmlns="http://www.w3.org/2000/svg" width="${imageSize}" height="${prevSvg ? imageSize * 2 : imageSize}" viewBox="0 0 ${imageSize} ${prevSvg ? imageSize * 2 : imageSize}"> <svg xmlns="http://www.w3.org/2000/svg" width="${imageSize}" height="${imageSize}" viewBox="0 0 ${imageSize} ${imageSize}">
<style> <style>
@media screen and (prefers-color-scheme: light) { @media screen and (prefers-color-scheme: light) {
#fallback-background { fill: transparent; } #fallback-background { fill: transparent; }
@@ -66,20 +52,20 @@ export default eventHandler(async (event) => {
<mask id="mask"> <mask id="mask">
<image <image
width="${imageSize}" width="${imageSize}"
height="${prevSvg ? imageSize * 2 : imageSize}" height="${imageSize}"
href="data:image/png;base64,${pngBuffer.toString('base64')}" href="data:image/png;base64,${pngBuffer.toString('base64')}"
image-rendering="pixelated" image-rendering="pixelated"
/> />
</mask> </mask>
<rect <rect
id="fallback-background" id="fallback-background"
width="100%" width="${imageSize}"
height="100%" ry="${imageSize / 24}" height="${imageSize}" ry="${imageSize / 24}"
fill="#fff" fill="#fff"
/> />
<rect <rect
width="100%" width="${imageSize}"
height="100%" height="${imageSize}"
fill="#000" fill="#000"
mask="url(#mask)" mask="url(#mask)"
/> />

View File

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

View File

@@ -1,55 +0,0 @@
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

@@ -101,7 +101,7 @@
}, },
{ {
"name": "navigation", "name": "navigation",
"title": "Navigation, Maps, and POIs" "title": "Navigation"
}, },
{ {
"name": "notifications", "name": "notifications",

View File

@@ -7,14 +7,6 @@
"dark": "/company-logos/vercel-dark.svg" "dark": "/company-logos/vercel-dark.svg"
} }
}, },
{
"name": "MDN Web Docs",
"url": "https://developer.mozilla.org/",
"image": {
"light": "/company-logos/mdn-light.svg",
"dark": "/company-logos/mdn-dark.svg"
}
},
{ {
"name": "Supabase", "name": "Supabase",
"url": "https://supabase.com", "url": "https://supabase.com",
@@ -31,14 +23,6 @@
"dark": "/company-logos/obsidian-dark.svg" "dark": "/company-logos/obsidian-dark.svg"
} }
}, },
{
"name": "Nuxt",
"url": "https://nuxt.com/",
"image": {
"light": "/company-logos/nuxt-light.svg",
"dark": "/company-logos/nuxt-dark.svg"
}
},
{ {
"name": "Open Collective", "name": "Open Collective",
"url": "https://opencollective.com", "url": "https://opencollective.com",

View File

@@ -77,8 +77,8 @@
"source": "https://github.com/swisnl/nuxt-lucide-icons", "source": "https://github.com/swisnl/nuxt-lucide-icons",
"documentation": "https://github.com/swisnl/nuxt-lucide-icons/blob/main/README.md" "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.", "description": "A library providing https://lucide.dev icons to lustre.",
"icon": "/framework-logos/lustre.webp", "icon": "/framework-logos/lustre.webp",
"shields": [ "shields": [
@@ -95,81 +95,5 @@
], ],
"source": "https://github.com/dinkelspiel/lucide_lustre", "source": "https://github.com/dinkelspiel/lucide_lustre",
"documentation": "https://github.com/dinkelspiel/lucide_lustre/blob/master/README.md" "documentation": "https://github.com/dinkelspiel/lucide_lustre/blob/master/README.md"
},
{
"name": "lucide_icons_flutter",
"description": "A library providing https://lucide.dev icons to Flutter.",
"icon": "/framework-logos/flutter.svg",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/pub/v/lucide_icons_flutter",
"href": "https://pub.dev/packages/lucide_icons_flutter"
},
{
"alt": "Total Downloads",
"src": "https://img.shields.io/pub/dm/lucide_icons_flutter",
"href": "https://pub.dev/packages/lucide_icons_flutter"
}
],
"source": "https://github.com/vqh2602/lucide-flutter-main",
"documentation": "https://pub.dev/documentation/lucide_icons_flutter/latest/"
},
{
"name": "lucide-slint",
"description": "Implementation of the lucide icon library for Slint.",
"icon": "/framework-logos/slint.svg",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/crates/v/lucide-slint",
"href": "https://crates.io/crates/lucide-slint"
},
{
"alt": "Total Downloads",
"src": "https://img.shields.io/crates/d/lucide-slint",
"href": "https://crates.io/crates/lucide-slint"
}
],
"source": "https://github.com/cnlancehu/lucide-slint",
"documentation": "https://github.com/cnlancehu/lucide-slint/blob/main/README.md"
},
{
"name": "lucide-go",
"description": "Implementation of Lucide icons for Go's html/template package.",
"icon": "/framework-logos/go.svg",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/github/v/release/kaugesaar/lucide-go",
"href": "https://github.com/kaugesaar/lucide-go/releases"
},
{
"alt": "Go Reference",
"src": "https://pkg.go.dev/badge/github.com/kaugesaar/lucide-go.svg",
"href": "https://pkg.go.dev/github.com/kaugesaar/lucide-go"
}
],
"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

@@ -53,8 +53,8 @@ const Backdrop = ({
<rect <rect
x="0" x="0"
y="0" y="0"
width="100%" width="24"
height="100%" height="24"
fill="#fff" fill="#fff"
stroke="none" stroke="none"
/> />
@@ -67,8 +67,8 @@ const Backdrop = ({
<rect <rect
x="0" x="0"
y="0" y="0"
width="100%" width="24"
height="100%" height="24"
opacity={0.5} opacity={0.5}
fill={`url(#pattern-${id})`} fill={`url(#pattern-${id})`}
stroke="none" stroke="none"

View File

@@ -7,17 +7,15 @@ const SvgPreview = React.forwardRef<
{ {
oldSrc: string; oldSrc: string;
newSrc: string; newSrc: string;
height: number;
width: number;
} & React.SVGProps<SVGSVGElement> } & React.SVGProps<SVGSVGElement>
>(({ oldSrc, newSrc, children, height, width, ...props }, ref) => { >(({ oldSrc, newSrc, children, ...props }, ref) => {
return ( return (
<svg <svg
ref={ref} ref={ref}
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width={width} width={24}
height={height} height={24}
viewBox={`0 0 ${width} ${height}`} viewBox="0 0 24 24"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
strokeWidth={2} strokeWidth={2}
@@ -27,8 +25,6 @@ const SvgPreview = React.forwardRef<
> >
<style>{darkModeCss}</style> <style>{darkModeCss}</style>
<Grid <Grid
width={width}
height={height}
strokeWidth={0.1} strokeWidth={0.1}
stroke="#777" stroke="#777"
strokeOpacity={0.3} strokeOpacity={0.3}
@@ -41,8 +37,8 @@ const SvgPreview = React.forwardRef<
<rect <rect
x="0" x="0"
y="0" y="0"
width="100%" width="24"
height="100%" height="24"
fill="#000" fill="#000"
stroke="none" stroke="none"
/> />

View File

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

View File

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

View File

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

View File

@@ -3,20 +3,12 @@ import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { search } from '../../../data/iconNodes' import { search } from '../../../data/iconNodes'
const SearchIcon = createLucideIcon('search', search) const SearchIcon = createLucideIcon('search', search)
defineProps({
shortcut: {
type: String,
required: false
}
})
</script> </script>
<template> <template>
<button class="fake-input"> <button class="fake-input">
<component :is="SearchIcon" class="search-icon"/> <component :is="SearchIcon" class="search-icon"/>
<slot/> <slot/>
<kbd v-if="shortcut" class="shortcut">{{ shortcut }}</kbd>
</button> </button>
</template> </template>
@@ -41,23 +33,4 @@ defineProps({
border-color: var(--vp-c-brand); border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt); background: var(--vp-c-bg-alt);
} }
.shortcut {
margin-left: auto;
padding: 2px 6px;
font-size: 12px;
font-family: inherit;
font-weight: 500;
line-height: 1.5;
color: var(--vp-c-text-3);
background: var(--vp-c-default-soft);
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
}
@media (hover: none) {
.shortcut {
display: none;
}
}
</style> </style>

View File

@@ -6,35 +6,20 @@ export default {
export interface InputProps { export interface InputProps {
type: string type: string
modelValue: string modelValue: string
shortcut?: string
} }
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, nextTick, watch } from 'vue' import { ref } from 'vue'
const props = withDefaults(defineProps<InputProps>(), { const props = withDefaults(defineProps<InputProps>(), {
type: 'text' type: 'text'
}) })
const input = ref() const input = ref()
const wrapperEl = ref()
const shortcutEl = ref()
defineEmits(['change', 'input', 'update:modelValue']) 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`)
}
})
}
onMounted(updateShortcutSpacing)
watch(() => props.shortcut, updateShortcutSpacing)
defineExpose({ defineExpose({
focus: () => { focus: () => {
input.value.focus() input.value.focus()
@@ -43,18 +28,17 @@ defineExpose({
</script> </script>
<template> <template>
<div class="input-wrapper" ref="wrapperEl"> <div class="input-wrapper">
<slot name="icon" class="icon" /> <slot name="icon" class="icon" />
<input <input
:type="type" :type="type"
class="input" class="input"
:class="{'has-icon': $slots.icon, 'has-shortcut': shortcut}" :class="{'has-icon': $slots.icon}"
ref="input" ref="input"
:value="modelValue" :value="modelValue"
v-bind="$attrs" v-bind="$attrs"
@input="$emit('update:modelValue', $event.target.value)" @input="$emit('update:modelValue', $event.target.value)"
/> />
<kbd v-if="shortcut" class="shortcut" ref="shortcutEl">{{ shortcut }}</kbd>
</div> </div>
</template> </template>
@@ -73,10 +57,6 @@ defineExpose({
font-size: 14px; font-size: 14px;
} }
.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); border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt); background: var(--vp-c-bg-alt);
@@ -86,28 +66,7 @@ defineExpose({
padding-left: 52px; padding-left: 52px;
} }
.shortcut {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
padding: 2px 6px;
font-size: 12px;
font-family: inherit;
font-weight: 500;
line-height: 1.5;
color: var(--vp-c-text-3);
background: var(--vp-c-default-soft);
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
pointer-events: none;
}
@media (hover: none) {
.shortcut {
display: none;
}
}
</style> </style>
<style> <style>

View File

@@ -14,7 +14,6 @@ const SearchIcon = createLucideIcon('search', search)
interface Props { interface Props {
modelValue: string modelValue: string
shortcut?: string
} }
const props = defineProps<Props>() const props = defineProps<Props>()
@@ -40,7 +39,6 @@ const value = computed({
ref="input" ref="input"
type="search" type="search"
autofocus autofocus
:shortcut="shortcut"
v-bind="$attrs" v-bind="$attrs"
v-model="value" v-model="value"
class="input-wrapper" class="input-wrapper"
@@ -73,4 +71,5 @@ const value = computed({
font-size: 14px; font-size: 14px;
height: 48px; height: 48px;
} }
</style> </style>

View File

@@ -5,15 +5,10 @@ import LucideIcon from '../base/LucideIcon.vue'
import { useRouter } from 'vitepress'; import { useRouter } from 'vitepress';
import { random } from 'lodash-es' import { random } from 'lodash-es'
import FakeInput from '../base/FakeInput.vue' import FakeInput from '../base/FakeInput.vue'
import useSearchShortcut from '../../utils/useSearchShortcut'
const { go } = useRouter() const { go } = useRouter()
const intervalTime = shallowRef() const intervalTime = shallowRef()
const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
go('/icons/?focus')
})
const getInitialItems = () => data.icons.slice(0, 48) const getInitialItems = () => data.icons.slice(0, 48)
const items = ref(getInitialItems()) const items = ref(getInitialItems())
let id = items.value.length + 1 let id = items.value.length + 1
@@ -69,11 +64,7 @@ onBeforeUnmount(() => {
</div> </div>
</TransitionGroup> </TransitionGroup>
</div> </div>
<FakeInput <FakeInput @click="go('/icons/?focus')" class="search-box">
@click="go('/icons/?focus')"
:shortcut="kbdSearchShortcut"
class="search-box"
>
Search {{ data.iconsCount }} icons... Search {{ data.iconsCount }} icons...
</FakeInput> </FakeInput>
</div> </div>

View File

@@ -178,8 +178,6 @@ const DiamondIcon = createLucideIcon('Diamond', diamond)
stroke-width: var(--customize-strokeWidth, 2); stroke-width: var(--customize-strokeWidth, 2);
width: calc(var(--customize-size, 24) * 1px); width: calc(var(--customize-size, 24) * 1px);
height: calc(var(--customize-size, 24) * 1px); height: calc(var(--customize-size, 24) * 1px);
max-width: 3rem;
max-height: 3rem;
} }
html.absolute-stroke-width .lucide-icon.customizable { html.absolute-stroke-width .lucide-icon.customizable {

View File

@@ -26,9 +26,10 @@ const iconComponent = computed(() => {
<component <component
ref="previewIcon" ref="previewIcon"
:is="iconComponent" :is="iconComponent"
:size="size" :width="size"
:color="color" :height="size"
:strokeWidth="absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth" :stroke="color"
:stroke-width="absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth"
/> />
<svg class="icon-grid" :viewBox="`0 0 ${size} ${size}`" fill="none" stroke-width="0.1" xmlns="http://www.w3.org/2000/svg"> <svg class="icon-grid" :viewBox="`0 0 ${size} ${size}`" fill="none" stroke-width="0.1" xmlns="http://www.w3.org/2000/svg">
<g :key="`grid-${i}`" v-for="(_, i) in gridLines"> <g :key="`grid-${i}`" v-for="(_, i) in gridLines">

View File

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

View File

@@ -2,18 +2,15 @@
import { ref, computed, defineAsyncComponent, onMounted, watch } from 'vue'; import { ref, computed, defineAsyncComponent, onMounted, watch } from 'vue';
import type { IconEntity } from '../../types'; import type { IconEntity } from '../../types';
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core'; import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
import { useRoute } from 'vitepress';
import IconGrid from './IconGrid.vue'; import IconGrid from './IconGrid.vue';
import InputSearch from '../base/InputSearch.vue'; import InputSearch from '../base/InputSearch.vue';
import useSearch from '../../composables/useSearch'; import useSearch from '../../composables/useSearch';
import useSearchInput from '../../composables/useSearchInput'; import useSearchInput from '../../composables/useSearchInput';
import useSearchShortcut from '../../utils/useSearchShortcut';
import StickyBar from './StickyBar.vue'; import StickyBar from './StickyBar.vue';
import useFetchTags from '../../composables/useFetchTags'; import useFetchTags from '../../composables/useFetchTags';
import useFetchCategories from '../../composables/useFetchCategories'; import useFetchCategories from '../../composables/useFetchCategories';
import chunkArray from '../../utils/chunkArray'; import chunkArray from '../../utils/chunkArray';
import CarbonAdOverlay from './CarbonAdOverlay.vue'; import CarbonAdOverlay from './CarbonAdOverlay.vue';
import useSearchPlaceholder from '../../utils/useSearchPlaceholder.ts';
const ICON_SIZE = 56; const ICON_SIZE = 56;
const ICON_GRID_GAP = 8; const ICON_GRID_GAP = 8;
@@ -37,10 +34,10 @@ const { execute: fetchTags, data: tags } = useFetchTags();
const { execute: fetchCategories, data: categories } = useFetchCategories(); const { execute: fetchCategories, data: categories } = useFetchCategories();
const overviewEl = ref<HTMLElement | null>(null); const overviewEl = ref<HTMLElement | null>(null);
const { width: containerWidth } = useElementSize(overviewEl); const { width: containerWidth } = useElementSize(overviewEl)
const columnSize = computed(() => { 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(() => { const mappedIcons = computed(() => {
@@ -61,38 +58,30 @@ const mappedIcons = computed(() => {
}); });
const { searchInput, searchQuery, searchQueryDebounced } = useSearchInput(); const { searchInput, searchQuery, searchQueryDebounced } = useSearchInput();
const { shortcutText: kbdSearchShortcut } = useSearchShortcut(() => {
searchInput.value?.focus();
});
const searchResults = useSearch(searchQueryDebounced, mappedIcons, [ const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
{ name: 'name', weight: 3 }, { name: 'name', weight: 3 },
{ name: 'aliases', weight: 3 }, { name: 'aliases', weight: 3 },
{ name: 'tags', weight: 2 }, { name: 'tags', weight: 2 },
{ name: 'categories', weight: 1 }, { name: 'categories', weight: 1 },
]); ]);
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
const chunkedIcons = computed(() => { const chunkedIcons = computed(() => {
return chunkArray(searchResults.value, columnSize.value); return chunkArray(searchResults.value, columnSize.value);
}); });
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(chunkedIcons, { const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
itemHeight: ICON_SIZE + ICON_GRID_GAP, chunkedIcons,
overscan: 10, {
}); itemHeight: ICON_SIZE + ICON_GRID_GAP,
overscan: 10
},
)
onMounted(() => { onMounted(() => {
containerProps.ref.value = document.documentElement; 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();
if (route.data?.relativePath && window.location.search.includes('focus')) {
searchInput.value?.focus();
}
});
function setActiveIconName(name: string) { function setActiveIconName(name: string) {
activeIconName.value = name; activeIconName.value = name;
@@ -112,8 +101,8 @@ const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue')); const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
watch(searchQueryDebounced, () => { watch(searchQueryDebounced, () => {
scrollTo(0); scrollTo(0)
}); })
function handleCloseDrawer() { function handleCloseDrawer() {
setActiveIconName(''); setActiveIconName('');
@@ -123,24 +112,19 @@ function handleCloseDrawer() {
</script> </script>
<template> <template>
<div <div ref="overviewEl" class="overview-container">
ref="overviewEl"
class="overview-container"
>
<StickyBar> <StickyBar>
<InputSearch <InputSearch
:placeholder="`Search ${icons.length} icons ...`" :placeholder="`Search ${icons.length} icons ...`"
v-model="searchQuery" v-model="searchQuery"
ref="searchInput" ref="searchInput"
:shortcut="kbdSearchShortcut"
class="input-wrapper" class="input-wrapper"
@focus="onFocusSearchInput" @focus="onFocusSearchInput"
/> />
</StickyBar> </StickyBar>
<NoResults <NoResults
v-if="searchPlaceholder.isNoResults" v-if="searchResults.length === 0 && searchQuery !== ''"
:searchQuery="searchPlaceholder.query" :searchQuery="searchQuery"
:isBrandSearch="searchPlaceholder.isBrand"
@clear="searchQuery = ''" @clear="searchQuery = ''"
/> />
<IconGrid <IconGrid
@@ -186,4 +170,8 @@ function handleCloseDrawer() {
.input-wrapper { .input-wrapper {
width: 100%; width: 100%;
} }
.overview-container {
padding-bottom: 288px;
}
</style> </style>

View File

@@ -1,218 +1,56 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, computed, markRaw, shallowReadonly, watch } from 'vue'; import { ref, onMounted, computed } from 'vue'
import { import { bird, squirrel, rabbit } from '../../../data/iconNodes'
bird, import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
squirrel, import {useEventListener} from '@vueuse/core'
rabbit, import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'
ghost, import { IconNode } from '../../types'
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';
const { searchQuery, isBrandSearch } = defineProps<{ defineProps<{
searchQuery: string; searchQuery: string
isBrandSearch: boolean; }>()
}>();
defineEmits(['clear']); defineEmits(['clear'])
interface Placeholder { const animalIcon = ref<HTMLElement>()
title: string; const randomAnimal = computed<IconNode>(() => {
message: string; return Math.random() > 0.5 ? squirrel : Math.random() > 0.5 ? rabbit : bird
icon: IconNode; })
finePrint?: string; const animalComponent = computed(() => createLucideIcon('animal', randomAnimal.value))
} const flip = ref(false)
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(() => { onMounted(() => {
useEventListener(document, 'mousemove', (mouseEvent) => { useEventListener(document, 'mousemove', (mouseEvent) => {
const { width, x } = placeholderIcon.value.getBoundingClientRect(); const {width, height, x, y} = animalIcon.value.getBoundingClientRect()
const centerX = width / 2 + x; const centerX = (width / 2) + x
flip.value = mouseEvent.x < centerX
})
})
flip.value = !isBrandSearch && mouseEvent.x < centerX;
});
});
</script> </script>
<template> <template>
<div class="no-results"> <div class="no-results">
<component <component
:is="iconComponent" :is="animalComponent"
class="placeholder-icon" class="animal-icon"
ref="placeholderIcon" ref="animalIcon"
:class="{ flip }" :class="{ flip }"
:strokeWidth="1" :strokeWidth="1"
/> />
<h2 class="no-results-text">{{ placeholder.title.replace('[name]', searchQuery) }}</h2> <h2 class="no-results-text">
<p class="no-results-message"> No icons found for '{{ searchQuery }}'
{{ placeholder.message.replace('[name]', searchQuery) }} </h2>
</p>
<div class="divider"></div>
<p
v-if="placeholder.finePrint"
class="no-results-fine-print"
>
{{ placeholder.finePrint }}
</p>
<VPButton <VPButton
v-if="isBrandSearch" text="Clear your search and try again"
text="Head over to Simple Icons" theme="alt"
theme="brand"
:href="`https://simpleicons.org/?q=${searchQuery}`"
target="_blank"
/>
<VPButton
v-else
text="Clear search & try again"
theme="brand"
@click="$emit('clear')" @click="$emit('clear')"
/> />
<span class="text-divider">or</span> <span class="text-divider">or</span>
<VPButton <VPButton
v-if="isBrandSearch" text="Search on Github issues"
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" theme="alt"
:href="`https://github.com/lucide-icons/lucide/issues?q=is%3Aopen+${searchQuery}`" :href="`https://github.com/lucide-icons/lucide/issues?q=is%3Aopen+${searchQuery}`"
target="_blank" target="_blank"
@@ -225,38 +63,33 @@ onMounted(() => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
text-align: center;
padding-block: 48px;
} }
.placeholder-icon { .animal-icon {
width: 96px; width: 160px;
height: 96px; height: 160px;
color: var(--vp-c-text-1); color: var(--vp-c-neutral);
opacity: 0.8;
margin-top: 72px;
} }
.placeholder-icon.flip { .animal-icon.flip {
transform: rotateY(180deg); transform: rotateY(180deg);
} }
@media (min-width: 960px) {
.animal-icon {
width: 240px;
height: 240px;
}
}
.no-results-text { .no-results-text {
line-height: 1.35; line-height: 40px;
font-size: 24px; font-size: 24px;
margin-top: 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; margin-bottom: 32px;
color: var(--vp-c-text-2); text-align: center;
text-wrap: balance;
} }
.text-divider { .text-divider {
@@ -264,10 +97,4 @@ onMounted(() => {
font-size: 16px; font-size: 16px;
color: var(--vp-c-neutral); color: var(--vp-c-neutral);
} }
.divider {
margin: 24px auto 18px;
width: 64px;
height: 1px;
background-color: var(--vp-c-divider);
}
</style> </style>

View File

@@ -110,7 +110,7 @@ const customizingActive = computed(() => {
name="size" name="size"
v-model="size" v-model="size"
:min="16" :min="16"
:max="256" :max="48"
:step="4" :step="4"
/> />
</InputField> </InputField>

View File

@@ -2,7 +2,7 @@
import { ref, inject, Ref } from 'vue'; import { ref, inject, Ref } from 'vue';
export const ICON_STYLE_CONTEXT = Symbol('style'); export const ICON_STYLE_CONTEXT = Symbol('size');
interface IconSizeContext { interface IconSizeContext {
size: Ref<number>; size: Ref<number>;

View File

@@ -1,41 +0,0 @@
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,35 +0,0 @@
import { ref, onMounted, onBeforeUnmount } from 'vue';
/**
* Composable for handling search keyboard shortcuts.
* Listens for Cmd/Ctrl+K and "/" keys to trigger a search action.
*
* @param callback - Function to execute when shortcut is triggered
* @returns Object containing the platform-specific shortcut display text
*/
export default function useSearchShortcut(callback: () => void) {
const shortcutText = ref('');
function handleKeydown(event: KeyboardEvent) {
// Check for Cmd+K (Mac), Ctrl+K (Windows/Linux), or forward slash
if (((event.metaKey || event.ctrlKey) && event.key === 'k') || event.key === '/') {
event.preventDefault();
callback();
}
}
onMounted(() => {
// Detect platform and set appropriate keyboard shortcut for search focus
const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
shortcutText.value = isMac ? '⌘K' : 'Ctrl+K';
// Add keyboard shortcut listener
window.addEventListener('keydown', handleKeydown);
});
onBeforeUnmount(() => {
window.removeEventListener('keydown', handleKeydown);
});
return { shortcutText };
}

View File

@@ -7,7 +7,7 @@ nextPage:
# What is Lucide? # What is Lucide?
Lucide is an open-source icon library that provides 1000+ vector (svg) files for displaying icons and symbols in digital and non-digital projects. The library aims to make it easier for designers and developers to incorporate icons into their projects by providing several official [packages](/packages). Lucide is an open-source icon library that provides 1000+ vector (svg) files for displaying icons and symbols in digital and non-digital projects. The library aims to make it easier for designers and developers to incorporate icons into their projects by providing several official [packages](/packages) to make it easier to use these icons in your project.
## Available Icons ## Available Icons

View File

@@ -11,7 +11,7 @@ Implementation of the lucide icon library for web applications.
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide pnpm install lucide
``` ```
```sh [yarn] ```sh [yarn]
@@ -37,7 +37,7 @@ Implementation of the lucide icon library for React applications.
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-react pnpm install lucide-react
``` ```
```sh [yarn] ```sh [yarn]
@@ -64,7 +64,7 @@ Implementation of the lucide icon library for Vue applications.
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-vue-next pnpm install lucide-vue-next
``` ```
```sh [yarn] ```sh [yarn]
@@ -91,7 +91,7 @@ Implementation of the lucide icon library for Svelte applications.
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-svelte pnpm install lucide-svelte
``` ```
```sh [yarn] ```sh [yarn]
@@ -117,7 +117,7 @@ Implementation of the lucide icon library for Solid applications.
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-solid pnpm install lucide-solid
``` ```
```sh [yarn] ```sh [yarn]
@@ -143,7 +143,7 @@ Implementation of the lucide icon library for Angular applications.
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-angular pnpm install lucide-angular
``` ```
```sh [yarn] ```sh [yarn]
@@ -169,7 +169,7 @@ Implementation of the lucide icon library for preact applications.
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-preact pnpm install lucide-preact
``` ```
```sh [yarn] ```sh [yarn]
@@ -196,7 +196,7 @@ Implementation of the lucide icon library for Astro applications.
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add @lucide/astro pnpm install @lucide/astro
``` ```
```sh [yarn] ```sh [yarn]
@@ -222,7 +222,7 @@ Implementation of the lucide icon library for multiple usages that like to use:
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-static pnpm install lucide-static
``` ```
```sh [yarn] ```sh [yarn]

View File

@@ -1,20 +1,13 @@
# Lucide Angular # Lucide Angular
Angular components and services for Lucide icons that integrate with Angular's dependency injection and component system. Provides both traditional module-based and modern standalone component approaches for maximum flexibility in Angular applications. Implementation of the lucide icon library for Angular applications.
**What you can accomplish:**
- Use icons as Angular components with full dependency injection support
- Configure icons globally through Angular services and providers
- Choose from multiple component selectors (lucide-angular, lucide-icon, i-lucide, span-lucide)
- Integrate with Angular's reactive forms and data binding
- Build scalable applications with tree-shaken icon bundles and lazy loading support
## Installation ## Installation
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-angular pnpm install lucide-angular
``` ```
```sh [yarn] ```sh [yarn]

View File

@@ -1,13 +1,6 @@
# Lucide Astro # Lucide Astro
Astro components for Lucide icons that work perfectly with Astro's island architecture and multi-framework support. Each icon is an Astro component that renders as an inline SVG, providing excellent performance for static sites and server-side rendering scenarios. Implementation of the lucide icon library for Astro applications.
**What you can accomplish:**
- Use icons as Astro components with zero JavaScript runtime overhead
- Build fast, static websites with optimized SVG icons
- Integrate seamlessly with Astro's component islands and partial hydration
- Create multi-framework applications where icons work across different UI libraries
- Optimize performance with direct icon imports and build-time rendering
## Installation ## Installation

View File

@@ -4,21 +4,14 @@ title: Lucide Preact
# Lucide Preact # Lucide Preact
Preact components for Lucide icons that provide React-like development experience with a smaller footprint. Each icon is a lightweight Preact component that renders as an inline SVG, perfect for applications that need React compatibility with minimal bundle size. Implementation of the lucide icon library for preact applications.
**What you can accomplish:**
- Use icons as Preact components with React-like syntax and patterns
- Build lightweight applications with Preact's smaller runtime
- Create fast, responsive interfaces with minimal JavaScript overhead
- Maintain React compatibility while reducing bundle size
- Integrate with existing Preact applications and component libraries
## Installation ## Installation
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-preact pnpm install lucide-preact
``` ```
```sh [yarn] ```sh [yarn]

View File

@@ -1,13 +1,6 @@
# Lucide React Native # Lucide React Native
React Native components for Lucide icons that work seamlessly across iOS and Android platforms. Built on top of react-native-svg, each icon renders as a native SVG component, providing consistent visual appearance and performance across mobile devices. Implementation of the lucide icon library for React Native applications
**What you can accomplish:**
- Use icons as React Native components with platform-consistent rendering
- Build cross-platform mobile apps with scalable vector icons
- Create responsive interfaces that adapt to different screen densities
- Integrate with React Native's styling system and animation libraries
- Maintain consistent icon appearance across iOS and Android platforms
## Installation ## Installation
@@ -16,7 +9,7 @@ First, ensure that you have `react-native-svg` (version between 12 and 15) insta
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-react-native pnpm install lucide-react-native
``` ```
```sh [yarn] ```sh [yarn]

View File

@@ -1,20 +1,13 @@
# Lucide React # Lucide React
React components for Lucide icons that integrate seamlessly into your React applications. Each icon is a fully-typed React component that renders as an optimized inline SVG, giving you the flexibility of components with the performance of vector graphics. Implementation of the lucide icon library for react applications
**What you can accomplish:**
- Import icons as React components with full TypeScript support
- Pass props to customize size, color, stroke width, and other SVG attributes
- Use icons in JSX with the same ease as any other React component
- Benefit from automatic tree-shaking to include only the icons you use
- Create dynamic icon components that respond to state and user interactions
## Installation ## Installation
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-react pnpm install lucide-react
``` ```
```sh [yarn] ```sh [yarn]

View File

@@ -1,20 +1,13 @@
# Lucide Solid # Lucide Solid
SolidJS components for Lucide icons that leverage Solid's fine-grained reactivity system. Each icon is a reactive Solid component that renders as an inline SVG, providing exceptional performance through Solid's compile-time optimizations and reactive primitives. Implementation of the lucide icon library for solid applications.
**What you can accomplish:**
- Use icons as SolidJS components with fine-grained reactivity
- Create highly performant interfaces with Solid's reactive system
- Build dynamic icon components that respond to signals and stores
- Integrate seamlessly with Solid's JSX and component patterns
- Optimize performance with direct icon imports and minimal runtime overhead
## Installation ## Installation
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-solid pnpm install lucide-solid
``` ```
```sh [yarn] ```sh [yarn]

View File

@@ -1,14 +1,5 @@
# Lucide Static # Lucide Static
Static assets and utilities for Lucide icons that work without JavaScript frameworks. This package provides multiple formats including individual SVG files, SVG sprites, icon fonts, and Node.js utilities for server-side rendering and static site generation.
**What you can accomplish:**
- Use individual SVG files as images or CSS background images
- Implement icon fonts for CSS-based icon systems
- Create SVG sprites for efficient icon loading in static sites
- Import SVG strings in Node.js applications and server-side rendering
- Build static websites and applications without JavaScript framework dependencies
This package includes the following implementations of Lucide icons: This package includes the following implementations of Lucide icons:
- Individual SVG files - Individual SVG files
@@ -45,7 +36,7 @@ For production environments, we recommend using a bundler with tree-shaking supp
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-static pnpm install lucide-static
``` ```
```sh [yarn] ```sh [yarn]
@@ -110,21 +101,12 @@ Make sure you have the correct Webpack loader configured, such as [`url-loader`]
To import an SVG as a string (e.g., for templating): To import an SVG as a string (e.g., for templating):
::: code-group ```js
```js [Webpack]
import arrowRightIcon from 'lucide-static/icons/arrow-right'; import arrowRightIcon from 'lucide-static/icons/arrow-right';
``` ```
```js [Vite]
import arrowRightIcon from 'lucide-static/icons/arrow-right.svg?raw';
```
:::
You'll need an SVG loader like [`svg-inline-loader`](https://v4.webpack.js.org/loaders/svg-inline-loader/). You'll need an SVG loader like [`svg-inline-loader`](https://v4.webpack.js.org/loaders/svg-inline-loader/).
### Using the SVG sprite ### Using the SVG sprite
:::danger :::danger
@@ -226,27 +208,19 @@ If you're not using a package manager, you can download the font files directly
## Node.js ## Node.js
You can also import Lucide icons in Node.js projects: You can also import Lucide icons in Node.js (CommonJS) projects:
::: code-group ```js
const {messageSquare} = require('lucide-static/lib');
```js [ESM]
import {MessageSquare} from 'lucide-static';
``` ```
```js [CommonJs] > Note: Each icon name is in camelCase.
const {MessageSquare} = require('lucide-static');
```
:::
> Note: Each icon name is in PascalCase.
#### Express app example in Node.js #### Express app example in Node.js
```js ```js
import express from 'express'; const express = require('express');
import {MessageSquare} from 'lucide-static'; const {messageSquare} = require('lucide-static/lib');
const app = express(); const app = express();
const port = 3000; const port = 3000;
@@ -259,7 +233,7 @@ app.get('/', (req, res) => {
</head> </head>
<body> <body>
<h1>Lucide Icons</h1> <h1>Lucide Icons</h1>
<p>This is a Lucide icon ${MessageSquare}</p> <p>This is a Lucide icon ${messageSquare}</p>
</body> </body>
</html> </html>

View File

@@ -1,13 +1,6 @@
# Lucide Svelte # Lucide Svelte
Svelte components for Lucide icons that work seamlessly with both Svelte 4 and Svelte 5. Each icon is a reactive Svelte component that renders as an inline SVG, providing excellent performance and integration with Svelte's reactive system and modern features. Implementation of the lucide icon library for svelte applications.
**What you can accomplish:**
- Use icons as Svelte components with full reactivity and TypeScript support
- Bind icon properties to reactive variables and stores
- Create dynamic icon systems that respond to application state
- Build type-safe interfaces with comprehensive TypeScript definitions
- Optimize bundle sizes with direct icon imports and tree-shaking
## Installation ## Installation

View File

@@ -1,20 +1,13 @@
# Lucide Vue Next # 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. Implementation of the lucide icon library for 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 ## Installation
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-vue-next pnpm install lucide-vue-next
``` ```
```sh [yarn] ```sh [yarn]

View File

@@ -1,13 +1,6 @@
# Lucide Vue # Lucide Vue
Vue 2 components for Lucide icons that integrate with Vue's Options API and template system. Each icon is a Vue component that renders as an inline SVG, providing familiar Vue development patterns for legacy applications still using Vue 2. Implementation of the lucide icon library for Vue applications.
**What you can accomplish:**
- Use icons as Vue 2 components with Options API integration
- Maintain legacy Vue 2 applications with modern icon components
- Integrate with Vue 2's template system and component lifecycle
- Build applications using Vue 2's familiar syntax and patterns
- Bridge the gap while planning migration to Vue 3
::: danger ::: danger
This package is deprecated. Vue 2 is EOF See [Announcement](https://v2.vuejs.org/eol/). Migrate to Vue 3. This package is deprecated. Vue 2 is EOF See [Announcement](https://v2.vuejs.org/eol/). Migrate to Vue 3.
@@ -18,7 +11,7 @@ This package is deprecated. Vue 2 is EOF See [Announcement](https://v2.vuejs.or
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide-vue pnpm install lucide-vue
``` ```
```sh [yarn] ```sh [yarn]

View File

@@ -1,13 +1,6 @@
# Lucide # Lucide
The core Lucide package for vanilla JavaScript applications. This package allows you to easily add scalable vector icons to any web project without framework dependencies. Perfect for static websites, legacy applications, or when you need lightweight icon integration with maximum browser compatibility. Implementation of the lucide icon library for web applications.
**What you can accomplish:**
- Add icons to HTML using simple data attributes
- Dynamically create and insert SVG icons with JavaScript
- Customize icon appearance with CSS classes and inline styles
- Tree-shake unused icons to keep bundle sizes minimal
- Use icons in any JavaScript environment or plain HTML
## Installation ## Installation
@@ -16,7 +9,7 @@ The core Lucide package for vanilla JavaScript applications. This package allows
::: code-group ::: code-group
```sh [pnpm] ```sh [pnpm]
pnpm add lucide pnpm install lucide
``` ```
```sh [yarn] ```sh [yarn]
@@ -95,12 +88,7 @@ createIcons({
### Additional Options ### Additional Options
In the `createIcons` function you can pass some extra parameters: In the `createIcons` function you can pass some extra parameters to adjust the `nameAttr` or add custom attributes like for example classes.
- you can pass `nameAttr` to adjust the attribute name to replace for
- you can pass `attrs` to pass additional custom attributes, for instance CSS classes or stroke options.
- you can pass `root` to provide a custom DOM element the icons should be replaced in (useful when manipulating small sections of a large DOM or elements in the shadow DOM)
- you can pass `inTemplates: true` to also replace icons inside `<template>` tags.
Here is a full example: Here is a full example:
@@ -113,9 +101,7 @@ createIcons({
'stroke-width': 1, 'stroke-width': 1,
stroke: '#333' stroke: '#333'
}, },
nameAttr: 'data-lucide', // attribute for the icon name. nameAttr: 'data-lucide' // attribute for the icon name.
root: element, // DOM element to replace icons in.
inTemplates: true // Also replace icons inside <template> tags.
}); });
``` ```
@@ -133,34 +119,6 @@ createIcons({
}); });
``` ```
### Custom Document root
Apply icons in a custom root element, for instance a shadow DOM root.
```js
import { createIcons } from 'lucide';
// Custom root element, for instance a shadow DOM root.
const shadowRoot = element.attachShadow({ mode: 'open' });
createIcons({
root: shadowRoot
});
```
### Apply icons inside `<template>` tags
By default icons inside `<template>` tags are not added.
By setting the `inTemplates` option to `true`, icons inside templates will also be replaced.
```js
import { createIcons } from 'lucide';
createIcons({
inTemplates: true
});
```
### Custom Element binding ### Custom Element binding
```js ```js

View File

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

View File

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

View File

@@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 361 104.2">
<g style="fill:#0F5DBD"><path d="M40.3 0 11.7 92.1H0L28.5 0h11.8zM50.7 0v92.1H40.3V0h10.4zM101.4 0v92.1H91V0h10.4zM91 0 62.5 92.1H50.8L79.3 0H91zM294 95.4h67v8.8h-67v-8.8z"/></g>
<g style="fill:#000"><path d="M198.7 72.8h-17.1v-5.5h3.8V51.5c0-3.7-.7-6.3-2.1-7.9-1.4-1.6-3.3-2.3-5.7-2.3-3.2 0-5.6 1.1-7.2 3.4s-2.4 4.6-2.5 6.9v15.6h6v5.5h-17.1v-5.5h3.8V51.5c0-3.8-.7-6.4-2.1-7.9-1.4-1.5-3.3-2.3-5.6-2.3-3.2 0-5.5 1.1-7.2 3.3-1.6 2.2-2.4 4.5-2.5 6.9v15.8h6.9v5.5h-20.2v-5.5h6V42h-6.1v-5.6h13.4v6.4c1.2-2.1 2.7-3.8 4.7-5.2 2-1.3 4.4-2 7.3-2 2.8 0 5.3.7 7.5 2.1 2.2 1.4 3.7 3.5 4.5 6.4 1.1-2.5 2.7-4.5 4.9-6.1 2.2-1.6 4.8-2.4 7.9-2.4 3.5 0 6.5 1.1 8.9 3.3 2.4 2.2 3.7 5.6 3.7 10.2v18.2h6.1v5.5zM241.2 72.8H228v-7.2c-1.2 2.2-2.8 4.1-4.9 5.6-2.1 1.6-4.8 2.4-8.3 2.4-4.8 0-8.7-1.6-11.6-4.9-2.9-3.2-4.3-7.7-4.3-13.3 0-5 1.3-9.6 4-13.7 2.6-4.1 6.9-6.2 12.8-6.2 5.7 0 9.8 2.2 12.3 6.5V22.3h-8.6v-5.6h15.8v50.6h6v5.5zM227.9 56v-4.4c-.1-3-1.2-5.5-3.2-7.3-2-1.8-4.4-2.8-7.2-2.8-3.6 0-6.3 1.3-8.2 3.9-1.9 2.6-2.8 5.8-2.8 9.6 0 4.1 1 7.3 3 9.5s4.5 3.3 7.4 3.3c3.2 0 5.8-1.3 7.8-3.8 2.1-2.6 3.1-5.3 3.2-8zM286.4 72.8h-19.3v-5.5h6V51.5c0-3.7-.7-6.3-2.2-7.9-1.4-1.6-3.4-2.3-5.7-2.3-3.1 0-5.6 1-7.4 3-1.8 2-2.8 4.4-2.9 7v15.9h6v5.5h-19.3v-5.5h6V42h-6.2v-5.6H255v6.2c2.6-4.6 6.8-6.9 12.7-6.9 3.6 0 6.7 1.1 9.2 3.3 2.5 2.2 3.7 5.6 3.7 10.2v18.2h6v5.4z"/></g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 361 104.2">
<g style="fill:#A4CEFE"><path d="M40.3 0 11.7 92.1H0L28.5 0h11.8zM50.7 0v92.1H40.3V0h10.4zM101.4 0v92.1H91V0h10.4zM91 0 62.5 92.1H50.8L79.3 0H91zM294 95.4h67v8.8h-67v-8.8z"/></g>
<g style="fill:#fff"><path d="M198.7 72.8h-17.1v-5.5h3.8V51.5c0-3.7-.7-6.3-2.1-7.9-1.4-1.6-3.3-2.3-5.7-2.3-3.2 0-5.6 1.1-7.2 3.4s-2.4 4.6-2.5 6.9v15.6h6v5.5h-17.1v-5.5h3.8V51.5c0-3.8-.7-6.4-2.1-7.9-1.4-1.5-3.3-2.3-5.6-2.3-3.2 0-5.5 1.1-7.2 3.3-1.6 2.2-2.4 4.5-2.5 6.9v15.8h6.9v5.5h-20.2v-5.5h6V42h-6.1v-5.6h13.4v6.4c1.2-2.1 2.7-3.8 4.7-5.2 2-1.3 4.4-2 7.3-2 2.8 0 5.3.7 7.5 2.1 2.2 1.4 3.7 3.5 4.5 6.4 1.1-2.5 2.7-4.5 4.9-6.1 2.2-1.6 4.8-2.4 7.9-2.4 3.5 0 6.5 1.1 8.9 3.3 2.4 2.2 3.7 5.6 3.7 10.2v18.2h6.1v5.5zM241.2 72.8H228v-7.2c-1.2 2.2-2.8 4.1-4.9 5.6-2.1 1.6-4.8 2.4-8.3 2.4-4.8 0-8.7-1.6-11.6-4.9-2.9-3.2-4.3-7.7-4.3-13.3 0-5 1.3-9.6 4-13.7 2.6-4.1 6.9-6.2 12.8-6.2 5.7 0 9.8 2.2 12.3 6.5V22.3h-8.6v-5.6h15.8v50.6h6v5.5zM227.9 56v-4.4c-.1-3-1.2-5.5-3.2-7.3-2-1.8-4.4-2.8-7.2-2.8-3.6 0-6.3 1.3-8.2 3.9-1.9 2.6-2.8 5.8-2.8 9.6 0 4.1 1 7.3 3 9.5s4.5 3.3 7.4 3.3c3.2 0 5.8-1.3 7.8-3.8 2.1-2.6 3.1-5.3 3.2-8zM286.4 72.8h-19.3v-5.5h6V51.5c0-3.7-.7-6.3-2.2-7.9-1.4-1.6-3.4-2.3-5.7-2.3-3.1 0-5.6 1-7.4 3-1.8 2-2.8 4.4-2.9 7v15.9h6v5.5h-19.3v-5.5h6V42h-6.2v-5.6H255v6.2c2.6-4.6 6.8-6.9 12.7-6.9 3.6 0 6.7 1.1 9.2 3.3 2.5 2.2 3.7 5.6 3.7 10.2v18.2h6v5.4z"/></g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="200" fill="none" class="text-highlighted block w-auto h-6" viewBox="0 0 800 200">
<path fill="#000" d="M377 200c2.16 0 4-1.791 4-4v-93s5 9 14 24l39 67c1.785 3.74 5.744 6 9 6h27V50h-27c-1.798 0-4 1.494-4 4v94l-18-32-36-61c-1.752-3.109-5.521-5-9-5h-26v150h27ZM726 92h13a6 6 0 0 0 6-6V60h28v32h27v24h-27v43c0 10.5 5.057 15 14 15h13v26h-17c-23.052 0-38-14.929-38-40v-44h-19V92ZM591 92v62c0 14.004-5.258 25.809-13 34-7.742 8.191-18.434 12-33 12-14.566 0-26.258-3.809-34-12-7.611-8.191-13-19.996-13-34V92h16c3.412 0 6.769.622 9 3 2.231 2.246 3 3.565 3 7v52c0 8.059.457 13.037 4 17 3.543 3.831 7.914 5 15 5 7.217 0 10.457-1.169 14-5 3.543-3.963 4-8.941 4-17v-52c0-3.435.769-5.622 3-8 1.96-2.09 4.028-2.04 7-2 .411.005 1.586 0 2 0h16ZM676 144l34-52h-26c-3.277 0-6.188 1.176-8 4l-16 24-15-23c-1.812-2.824-5.723-5-9-5h-25l34 51-37 57h26c3.25 0 6.182-3.213 8-6l18-27 19 28c1.818 2.787 4.75 5 8 5h26l-37-56Z"/>
<path fill="#00DC82" d="M168 200h111c3.542 0 6.932-1.244 10-3 3.068-1.756 6.23-3.959 8-7 1.77-3.041 3.002-6.49 3-10.001-.002-3.511-1.227-6.959-3-9.998L222 41c-1.77-3.04-3.933-5.245-7-7s-7.458-3-11-3-6.933 1.245-10 3-5.23 3.96-7 7l-19 33-38-64.002c-1.772-3.04-3.932-6.243-7-7.998s-6.458-2-10-2-6.932.245-10 2c-3.068 1.755-6.228 4.958-8 7.998L2 170c-1.773 3.039-1.998 6.487-2 9.998-.002 3.511.23 6.96 2 10.001 1.77 3.04 4.932 5.244 8 7 3.068 1.756 6.458 3 10 3h70c27.737 0 47.925-12.442 62-36l34-59 18-31 55 94h-73l-18 32Zm-79-32H40l73-126 37 63-24.509 42.725C116.144 163.01 105.488 168 89 168Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="200" fill="none" class="text-highlighted block w-auto h-6" viewBox="0 0 800 200">
<path fill="#fff" d="M377 200c2.16 0 4-1.791 4-4v-93s5 9 14 24l39 67c1.785 3.74 5.744 6 9 6h27V50h-27c-1.798 0-4 1.494-4 4v94l-18-32-36-61c-1.752-3.109-5.521-5-9-5h-26v150h27ZM726 92h13a6 6 0 0 0 6-6V60h28v32h27v24h-27v43c0 10.5 5.057 15 14 15h13v26h-17c-23.052 0-38-14.929-38-40v-44h-19V92ZM591 92v62c0 14.004-5.258 25.809-13 34-7.742 8.191-18.434 12-33 12-14.566 0-26.258-3.809-34-12-7.611-8.191-13-19.996-13-34V92h16c3.412 0 6.769.622 9 3 2.231 2.246 3 3.565 3 7v52c0 8.059.457 13.037 4 17 3.543 3.831 7.914 5 15 5 7.217 0 10.457-1.169 14-5 3.543-3.963 4-8.941 4-17v-52c0-3.435.769-5.622 3-8 1.96-2.09 4.028-2.04 7-2 .411.005 1.586 0 2 0h16ZM676 144l34-52h-26c-3.277 0-6.188 1.176-8 4l-16 24-15-23c-1.812-2.824-5.723-5-9-5h-25l34 51-37 57h26c3.25 0 6.182-3.213 8-6l18-27 19 28c1.818 2.787 4.75 5 8 5h26l-37-56Z"/>
<path fill="#00DC82" d="M168 200h111c3.542 0 6.932-1.244 10-3 3.068-1.756 6.23-3.959 8-7 1.77-3.041 3.002-6.49 3-10.001-.002-3.511-1.227-6.959-3-9.998L222 41c-1.77-3.04-3.933-5.245-7-7s-7.458-3-11-3-6.933 1.245-10 3-5.23 3.96-7 7l-19 33-38-64.002c-1.772-3.04-3.932-6.243-7-7.998s-6.458-2-10-2-6.932.245-10 2c-3.068 1.755-6.228 4.958-8 7.998L2 170c-1.773 3.039-1.998 6.487-2 9.998-.002 3.511.23 6.96 2 10.001 1.77 3.04 4.932 5.244 8 7 3.068 1.756 6.458 3 10 3h70c27.737 0 47.925-12.442 62-36l34-59 18-31 55 94h-73l-18 32Zm-79-32H40l73-126 37 63-24.509 42.725C116.144 163.01 105.488 168 89 168Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,55 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 205.4 76.7" style="enable-background:new 0 0 205.4 76.7;" xml:space="preserve">
<style type="text/css">
.st0{fill:#00ACD7;}
</style>
<g>
<g>
<g>
<g>
<path class="st0" d="M15.5,23.2c-0.4,0-0.5-0.2-0.3-0.5l2.1-2.7c0.2-0.3,0.7-0.5,1.1-0.5h35.7c0.4,0,0.5,0.3,0.3,0.6l-1.7,2.6
c-0.2,0.3-0.7,0.6-1,0.6L15.5,23.2z"/>
</g>
</g>
</g>
<g>
<g>
<g>
<path class="st0" d="M0.4,32.4c-0.4,0-0.5-0.2-0.3-0.5l2.1-2.7c0.2-0.3,0.7-0.5,1.1-0.5h45.6c0.4,0,0.6,0.3,0.5,0.6l-0.8,2.4
c-0.1,0.4-0.5,0.6-0.9,0.6L0.4,32.4z"/>
</g>
</g>
</g>
<g>
<g>
<g>
<path class="st0" d="M24.6,41.6c-0.4,0-0.5-0.3-0.3-0.6l1.4-2.5c0.2-0.3,0.6-0.6,1-0.6h20c0.4,0,0.6,0.3,0.6,0.7L47.1,41
c0,0.4-0.4,0.7-0.7,0.7L24.6,41.6z"/>
</g>
</g>
</g>
<g>
<g id="CXHf1q_3_">
<g>
<g>
<path class="st0" d="M128.4,21.4c-6.3,1.6-10.6,2.8-16.8,4.4c-1.5,0.4-1.6,0.5-2.9-1c-1.5-1.7-2.6-2.8-4.7-3.8
c-6.3-3.1-12.4-2.2-18.1,1.5c-6.8,4.4-10.3,10.9-10.2,19c0.1,8,5.6,14.6,13.5,15.7c6.8,0.9,12.5-1.5,17-6.6
c0.9-1.1,1.7-2.3,2.7-3.7c-3.6,0-8.1,0-19.3,0c-2.1,0-2.6-1.3-1.9-3c1.3-3.1,3.7-8.3,5.1-10.9c0.3-0.6,1-1.6,2.5-1.6
c5.1,0,23.9,0,36.4,0c-0.2,2.7-0.2,5.4-0.6,8.1c-1.1,7.2-3.8,13.8-8.2,19.6c-7.2,9.5-16.6,15.4-28.5,17
c-9.8,1.3-18.9-0.6-26.9-6.6c-7.4-5.6-11.6-13-12.7-22.2c-1.3-10.9,1.9-20.7,8.5-29.3c7.1-9.3,16.5-15.2,28-17.3
c9.4-1.7,18.4-0.6,26.5,4.9c5.3,3.5,9.1,8.3,11.6,14.1C130,20.6,129.6,21.1,128.4,21.4z"/>
</g>
<g>
<path class="st0" d="M161.5,76.7c-9.1-0.2-17.4-2.8-24.4-8.8c-5.9-5.1-9.6-11.6-10.8-19.3c-1.8-11.3,1.3-21.3,8.1-30.2
c7.3-9.6,16.1-14.6,28-16.7c10.2-1.8,19.8-0.8,28.5,5.1c7.9,5.4,12.8,12.7,14.1,22.3c1.7,13.5-2.2,24.5-11.5,33.9
c-6.6,6.7-14.7,10.9-24,12.8C166.8,76.3,164.1,76.4,161.5,76.7z M185.3,36.3c-0.1-1.3-0.1-2.3-0.3-3.3
c-1.8-9.9-10.9-15.5-20.4-13.3c-9.3,2.1-15.3,8-17.5,17.4c-1.8,7.8,2,15.7,9.2,18.9c5.5,2.4,11,2.1,16.3-0.6
C180.5,51.3,184.8,44.9,185.3,36.3z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -1,10 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -1,7 +0,0 @@
<svg width="35" height="35" xmlns="http://www.w3.org/2000/svg">
<defs />
<path fill="#2379f4" stroke="none"
d="M 10.65 32.39 L 27.6 20.64 C 27.6 20.64 28.36 20.2 28.36 19.51 C 28.36 18.59 27.390001 18.29 27.390001 18.29 L 18.06 14.6 C 17.73 14.47 17.27 14.84 17.7 15.3 L 20.79 18.39 C 20.79 18.39 21.65 19.23 21.65 19.79 C 21.65 20.35 21.12 20.84 21.12 20.84 L 9.89 31.7 C 9.49 32.09 10.03 32.79 10.64 32.41 Z" />
<path fill="#2379f4" stroke="none"
d="M 24.35 2.61 L 7.4 14.35 C 7.4 14.35 6.64 14.789999 6.64 15.48 C 6.64 16.4 7.61 16.7 7.61 16.7 L 16.94 20.39 C 17.27 20.52 17.73 20.15 17.3 19.69 L 14.210001 16.59 C 14.210001 16.59 13.35 15.750001 13.35 15.19 C 13.35 14.630001 13.88 14.14 13.88 14.14 L 25.09 3.32 C 25.500002 2.93 24.96 2.23 24.340002 2.61 Z" />
</svg>

Before

Width:  |  Height:  |  Size: 800 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -1,15 +0,0 @@
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

@@ -2,15 +2,14 @@
"$schema": "../icon.schema.json", "$schema": "../icon.schema.json",
"contributors": [ "contributors": [
"colebemis", "colebemis",
"jguddas", "ericfennis"
"karsa-mistmere"
], ],
"tags": [ "tags": [
"time", "text",
"watch", "alignment",
"alarm" "center"
], ],
"categories": [ "categories": [
"time" "text"
] ]
} }

View File

@@ -9,7 +9,7 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="M21 5H3" />
<path d="M17 12H7" /> <path d="M17 12H7" />
<path d="M19 19H5" /> <path d="M19 18H5" />
<path d="M21 6H3" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 279 B

After

Width:  |  Height:  |  Size: 279 B

17
icons/align-justify.json Normal file
View File

@@ -0,0 +1,17 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"ericfennis"
],
"tags": [
"text",
"alignment",
"justified",
"menu",
"list"
],
"categories": [
"text"
]
}

View File

@@ -9,7 +9,7 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="M3 5h18" />
<path d="M3 12h18" /> <path d="M3 12h18" />
<path d="M3 19h18" /> <path d="M3 18h18" />
<path d="M3 6h18" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 279 B

After

Width:  |  Height:  |  Size: 279 B

View File

@@ -1,15 +1,16 @@
{ {
"$schema": "../icon.schema.json", "$schema": "../icon.schema.json",
"contributors": [ "contributors": [
"karsa-mistmere" "colebemis",
"ericfennis"
], ],
"tags": [ "tags": [
"piece", "text",
"horse", "alignment",
"board game" "left",
"list"
], ],
"categories": [ "categories": [
"gaming", "text"
"emoji"
] ]
} }

View File

@@ -9,7 +9,7 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="M21 5H3" />
<path d="M15 12H3" /> <path d="M15 12H3" />
<path d="M17 19H3" /> <path d="M17 18H3" />
<path d="M21 6H3" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 279 B

After

Width:  |  Height:  |  Size: 279 B

View File

@@ -1,14 +1,15 @@
{ {
"$schema": "../icon.schema.json", "$schema": "../icon.schema.json",
"contributors": [ "contributors": [
"karsa-mistmere" "colebemis",
"ericfennis"
], ],
"tags": [ "tags": [
"piece", "text",
"board game" "alignment",
"right"
], ],
"categories": [ "categories": [
"gaming", "text"
"emoji"
] ]
} }

View File

@@ -9,7 +9,7 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="M21 5H3" />
<path d="M21 12H9" /> <path d="M21 12H9" />
<path d="M21 19H7" /> <path d="M21 18H7" />
<path d="M21 6H3" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 279 B

After

Width:  |  Height:  |  Size: 279 B

View File

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

View File

@@ -9,6 +9,6 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="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="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> </svg>

Before

Width:  |  Height:  |  Size: 337 B

After

Width:  |  Height:  |  Size: 339 B

View File

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

View File

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

Before

Width:  |  Height:  |  Size: 334 B

After

Width:  |  Height:  |  Size: 309 B

View File

@@ -5,27 +5,7 @@
], ],
"tags": [ "tags": [
"fruit", "fruit",
"food", "food"
"healthy",
"snack",
"nutrition",
"fresh",
"produce",
"grocery",
"organic",
"harvest",
"vitamin",
"red",
"green",
"juicy",
"sweet",
"tart",
"bite",
"orchard",
"plant",
"core",
"raw",
"diet"
], ],
"categories": [ "categories": [
"food-beverage" "food-beverage"

View File

@@ -9,6 +9,6 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
> >
<path d="M12 6.528V3a1 1 0 0 1 1-1h0" /> <path d="M12 20.94c1.5 0 2.75 1.06 4 1.06 3 0 6-8 6-12.22A4.91 4.91 0 0 0 17 5c-2.22 0-4 1.44-5 2-1-.56-2.78-2-5-2a4.9 4.9 0 0 0-5 4.78C2 14 5 22 8 22c1.25 0 2.5-1.06 4-1.06Z" />
<path d="M18.237 21A15 15 0 0 0 22 11a6 6 0 0 0-10-4.472A6 6 0 0 0 2 11a15.1 15.1 0 0 0 3.763 10 3 3 0 0 0 3.648.648 5.5 5.5 0 0 1 5.178 0A3 3 0 0 0 18.237 21" /> <path d="M10 2c1 .5 2 2 2 5" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 416 B

After

Width:  |  Height:  |  Size: 423 B

View File

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

View File

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

View File

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

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