Merge branch 'main' of https://github.com/lucide-icons/lucide into better-icon-detail-page
@@ -9,3 +9,4 @@ strikethrough
|
||||
touchpad
|
||||
ungroup
|
||||
toc
|
||||
sunlounger
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
dist
|
||||
build
|
||||
coverage
|
||||
lib
|
||||
tests
|
||||
node_modules
|
||||
.eslintrc.js
|
||||
docs/images
|
||||
docs/**/examples/
|
||||
packages/lucide-react/dynamicIconImports.js
|
||||
packages/angular/.angular
|
||||
75
.eslintrc.js
@@ -1,75 +0,0 @@
|
||||
const DEFAULT_ATTRS = require('./tools/build-icons/render/default-attrs.json');
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
},
|
||||
extends: ['airbnb-base', 'prettier'],
|
||||
plugins: ['import', '@html-eslint'],
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'no-shadow': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
devDependencies: ['**/*.test.js', '**/*.spec.js', '**/scripts/**'],
|
||||
},
|
||||
],
|
||||
'import/extensions': [
|
||||
'error',
|
||||
{
|
||||
pattern: {
|
||||
mjs: 'always',
|
||||
json: 'always',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./docs/tsconfig.json', './packages/*/tsconfig.json'],
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['./icons/*.svg'],
|
||||
parser: '@html-eslint/parser',
|
||||
rules: {
|
||||
'@html-eslint/require-doctype': 'off',
|
||||
'@html-eslint/no-duplicate-attrs': 'error',
|
||||
'@html-eslint/no-inline-styles': 'error',
|
||||
'@html-eslint/require-attrs': [
|
||||
'error',
|
||||
...Object.entries(DEFAULT_ATTRS).map(([attr, value]) => ({
|
||||
tag: 'svg',
|
||||
attr,
|
||||
value: String(value),
|
||||
})),
|
||||
],
|
||||
'@html-eslint/indent': ['error', 2],
|
||||
'@html-eslint/no-multiple-empty-lines': ['error', { max: 0 }],
|
||||
'@html-eslint/no-extra-spacing-attrs': [
|
||||
'error',
|
||||
{
|
||||
enforceBeforeSelfClose: true,
|
||||
},
|
||||
],
|
||||
'@html-eslint/require-closing-tags': [
|
||||
'error',
|
||||
{
|
||||
selfClosing: 'always',
|
||||
allowSelfClosingCustom: true,
|
||||
},
|
||||
],
|
||||
'@html-eslint/element-newline': 'error',
|
||||
'@html-eslint/no-trailing-spaces': 'error',
|
||||
'@html-eslint/quotes': 'error',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
50
.github/DISCUSSION_TEMPLATE/icon-design.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: Icon request
|
||||
description: Suggest a new icon for this project
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Before submitting an icon request check if it has already been requested. If there is an open request, please add a 👍.
|
||||
|
||||
> [!CAUTION]
|
||||
> 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 have been phased out. Consider using https://simpleicons.org, which offers purpose-built SVGs that are also on a 24×24px grid.
|
||||
- type: input
|
||||
id: name
|
||||
attributes:
|
||||
label: Icon name
|
||||
description: What should this icon depict? For multiple icons, provide a comma-separated list.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: use-cases
|
||||
attributes:
|
||||
label: Use cases
|
||||
description: Why do you need this icon? Include at least two real-life use cases per requested icon, avoiding generic descriptions like "it's a car icon".
|
||||
placeholder: e.g. I need a star icon to use in my rating system.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: design-ideas
|
||||
attributes:
|
||||
label: Design ideas
|
||||
description: What should this icon look like? Provide simple, minimalistic icon examples from other sets or your own drafts to help us visualize your request.
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Please review the following checklist before submitting your request.
|
||||
options:
|
||||
- label: I have searched if someone has submitted a similar request before and there weren't any.
|
||||
required: true
|
||||
- label: I have searched existing icons to make sure it does not already exist and I didn't find any.
|
||||
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
|
||||
required: true
|
||||
- label: I am not requesting an icon that includes religious, war/violence related, political imagery or hate symbols.
|
||||
required: true
|
||||
- label: I have provided appropriate use cases for the icon(s) requested.
|
||||
required: true
|
||||
106
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
groups:
|
||||
react-deps:
|
||||
patterns:
|
||||
- '@testing-library/react'
|
||||
- '@types/prop-types'
|
||||
- '@types/react'
|
||||
- '@vitejs/plugin-react'
|
||||
- 'react'
|
||||
- 'react-dom'
|
||||
vue-deps:
|
||||
patterns:
|
||||
- '@testing-library/vue'
|
||||
- '@vitejs/plugin-vue'
|
||||
- '@vue/test-utils'
|
||||
- 'vue'
|
||||
svelte-deps:
|
||||
patterns:
|
||||
- '@sveltejs/*'
|
||||
- '@testing-library/svelte'
|
||||
- '@tsconfig/svelte'
|
||||
- 'svelte'
|
||||
- 'svelte-check'
|
||||
- 'svelte-preprocess'
|
||||
solid-deps:
|
||||
patterns:
|
||||
- '@babel/*'
|
||||
- '@rollup/plugin-babel'
|
||||
- '@solidjs/testing-library'
|
||||
- 'babel-preset-solid'
|
||||
- 'solid-js'
|
||||
- 'vite-plugin-solid'
|
||||
react-native-deps:
|
||||
patterns:
|
||||
- 'react-native'
|
||||
- 'react-native-svg'
|
||||
angular-deps:
|
||||
patterns:
|
||||
- '@angular/*'
|
||||
- '@angular-eslint/*'
|
||||
- '@vitest/browser-playwright'
|
||||
- 'angular-eslint'
|
||||
- 'ng-packagr'
|
||||
- 'rxjs'
|
||||
- 'tslib'
|
||||
preact-deps:
|
||||
patterns:
|
||||
- '@preact/preset-vite'
|
||||
- '@testing-library/preact'
|
||||
- 'preact'
|
||||
astro-deps:
|
||||
patterns:
|
||||
- '@astrojs/*'
|
||||
- 'astro'
|
||||
testing-deps:
|
||||
patterns:
|
||||
- '@testing-library/*'
|
||||
- '@vitest/*'
|
||||
- 'jest-serializer-html'
|
||||
- 'jsdom'
|
||||
- 'linkedom'
|
||||
- 'vitest'
|
||||
build-deps:
|
||||
patterns:
|
||||
- '@rollup/*'
|
||||
- '@vitejs/*'
|
||||
- 'esbuild'
|
||||
- 'rollup'
|
||||
- 'rollup-plugin-*'
|
||||
- 'typescript'
|
||||
- 'vite'
|
||||
- 'vite-plugin-*'
|
||||
linting-deps:
|
||||
patterns:
|
||||
- '@eslint/*'
|
||||
- '@html-eslint/*'
|
||||
- '@typescript-eslint/*'
|
||||
- 'angular-eslint'
|
||||
- 'cspell'
|
||||
- 'eslint'
|
||||
- 'eslint-*'
|
||||
- 'husky'
|
||||
- 'lint-staged'
|
||||
- 'prettier'
|
||||
- 'prettier-plugin-*'
|
||||
types-deps:
|
||||
patterns:
|
||||
- '@types/*'
|
||||
remaining-deps:
|
||||
patterns:
|
||||
- '*'
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- '*'
|
||||
@@ -13,7 +13,7 @@ The `contributors` property is a required array of GitHub usernames for the peop
|
||||
|
||||
The `tags` property is an array of strings that describe the icon and can be used for searching.
|
||||
Validate the tags against the `icon.schema.json` to ensure they are correctly formatted and adhere to the defined structure.
|
||||
Provide tag suggestions based on the name of the icon and the use cases provided in the PR description. Use the existing tags in the repository as a reference for consistency and to avoid duplicates. Don't suggest words like: 'icon' and preferably use single words. Tags should always be in lowercase and should not contain spaces. The name of icon should not be included in the tags, as it is already specified.
|
||||
Provide tag suggestions based on the name of the icon and the use cases provided in the PR description. Use the existing tags in the repository as a reference for consistency and to avoid duplicates. Don't suggest words like: 'icon' and preferably use single words. Tags should always be in lowercase, and may also contain spaces (e.g. `magnifying glass`). The name of icon should not be included in the tags, as it is already specified.
|
||||
|
||||
# Categories
|
||||
|
||||
|
||||
2
.github/pull_request_template.md
vendored
@@ -47,7 +47,9 @@ Common scopes: icons, docs, studio, site, dev
|
||||
|
||||
### Design <!-- ONLY for new icons -->
|
||||
<!-- All of these requirements must be fulfilled. -->
|
||||
|
||||
- [ ] I've read and followed the [icon design guidelines](https://lucide.dev/contribute/icon-design-guide)
|
||||
- [ ] I've made sure that the icons don't [already exist](https://lucide.dev/icons), including the [Lab directory](https://github.com/lucide-icons/lucide/tree/main/lab).
|
||||
- [ ] I've made sure that the icons look sharp on low DPI displays.
|
||||
- [ ] I've made sure that the icons look consistent with the icon set in size, optical volume and density.
|
||||
- [ ] I've made sure that the icons are visually centered.
|
||||
|
||||
15
.github/workflows/angular.yml
vendored
@@ -7,13 +7,16 @@ on:
|
||||
- tools/build-icons/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -27,9 +30,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
30
.github/workflows/ci.yml
vendored
@@ -23,13 +23,7 @@ on:
|
||||
- beta
|
||||
- alpha
|
||||
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: write
|
||||
|
||||
# This is added to make sure we wait until last release is done, this is needed for the font-build process
|
||||
concurrency:
|
||||
group: continuous-integration
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
check-dispatch-gate:
|
||||
@@ -37,10 +31,12 @@ jobs:
|
||||
startsWith(github.event.head_commit.message, 'feat(icons)') &&
|
||||
github.event_name != 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
RELEASE_ENVIRONMENT: ${{ steps.environment.outputs.RELEASE_ENVIRONMENT }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Fetch tags
|
||||
run: git fetch --all --tags
|
||||
@@ -64,12 +60,14 @@ jobs:
|
||||
needs: check-dispatch-gate
|
||||
environment: ${{ needs.check-dispatch-gate.outputs.RELEASE_ENVIRONMENT }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # Required to create the release and tag
|
||||
outputs:
|
||||
VERSION: ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -104,7 +102,7 @@ jobs:
|
||||
echo '${{ steps.change-log.outputs.CHANGE_LOG }}'
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
|
||||
with:
|
||||
tag_name: ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||
name: Version ${{ steps.new-version.outputs.NEW_VERSION }}
|
||||
@@ -115,13 +113,19 @@ jobs:
|
||||
needs: create-release
|
||||
uses: './.github/workflows/release.yml'
|
||||
secrets: inherit
|
||||
permissions:
|
||||
id-token: write # Required for OIDC (npm provenance)
|
||||
contents: write # Required to upload release assets
|
||||
with:
|
||||
version: ${{ needs.create-release.outputs.VERSION }}
|
||||
|
||||
dispatch-release:
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
if: github.repository == 'lucide-icons/lucide' && github.event_name == 'workflow_dispatch'
|
||||
uses: './.github/workflows/release.yml'
|
||||
secrets: inherit
|
||||
permissions:
|
||||
id-token: write # Required for OIDC (npm provenance)
|
||||
contents: write # Required to upload release assets
|
||||
with:
|
||||
version: ${{ inputs.version }}
|
||||
tag: ${{ inputs.tag }}
|
||||
|
||||
@@ -4,14 +4,21 @@ on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
jobs:
|
||||
block_phrases:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Load stopwords from JSON & check issue title & body
|
||||
if: contains(github.event.issue.labels.*.name, '🙌 icon request')
|
||||
@@ -33,7 +40,7 @@ jobs:
|
||||
|
||||
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)
|
||||
[Click here to read our official statement about brand logos in Lucide.](https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md)
|
||||
|
||||
You can [search for similar issues.](https://github.com/lucide-icons/lucide/issues?q=is%3Aissue+${VALUE})
|
||||
|
||||
@@ -43,6 +50,3 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
5
.github/workflows/close-stale-prs.yml
vendored
@@ -3,13 +3,16 @@ on:
|
||||
schedule:
|
||||
- cron: '45 1 * * *'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-pr-message: This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 5 days.
|
||||
|
||||
13
.github/workflows/comment-icon-preview.yml
vendored
@@ -6,15 +6,22 @@ on:
|
||||
types:
|
||||
- completed
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read # Required to download the artifact from the triggering run
|
||||
pull-requests: write
|
||||
if: >
|
||||
github.repository == 'lucide-icons/lucide' &&
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
steps:
|
||||
- name: 'Download artifact'
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
@@ -42,7 +49,7 @@ jobs:
|
||||
id: pr-number
|
||||
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v2
|
||||
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0
|
||||
id: pr-comment
|
||||
with:
|
||||
issue-number: ${{ steps.pr-number.outputs.number }}
|
||||
@@ -50,7 +57,7 @@ jobs:
|
||||
body-includes: Added or changed icons
|
||||
|
||||
- name: Create or update comment
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
|
||||
with:
|
||||
comment-id: ${{ steps.pr-comment.outputs.comment-id }}
|
||||
issue-number: ${{ steps.pr-number.outputs.number }}
|
||||
|
||||
15
.github/workflows/icons.yml
vendored
@@ -9,13 +9,16 @@ on:
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -29,9 +32,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
7
.github/workflows/labeler.yml
vendored
@@ -2,12 +2,15 @@ name: 'Pull Request Labeler'
|
||||
on:
|
||||
- pull_request_target
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/labeler@v5
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0
|
||||
|
||||
13
.github/workflows/lint-code.yml
vendored
@@ -1,17 +1,18 @@
|
||||
name: Linting PR
|
||||
name: Linting PR code
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- icons/*.svg
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint-code:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
8
.github/workflows/lint-pr-title.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Linting PR
|
||||
name: Linting PR title
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -7,12 +7,16 @@ on:
|
||||
- edited
|
||||
- reopened
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read # Required to read the PR title
|
||||
|
||||
jobs:
|
||||
lint-pr-title:
|
||||
name: PR Title Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@v5
|
||||
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
||||
40
.github/workflows/linting-icons.yml
vendored
@@ -7,6 +7,9 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'icons/*'
|
||||
- 'lab/*'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
lint-filenames:
|
||||
@@ -16,27 +19,54 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v46
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files: icons/*
|
||||
files: |
|
||||
icons/*
|
||||
lab/*
|
||||
|
||||
- name: Generate annotations
|
||||
run: node ./scripts/lintFilenames.mts
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
|
||||
check-lab-icons:
|
||||
name: Check Lab Icons
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Check lab icons do not already exist in icons
|
||||
run: node ./scripts/checkLabIcons.mts
|
||||
|
||||
lint-aliases:
|
||||
name: Check Uniqueness of Aliases
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- 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 '"
|
||||
|
||||
15
.github/workflows/lucide-astro.yml
vendored
@@ -11,13 +11,16 @@ on:
|
||||
- tools/build-icons/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
cache: 'pnpm'
|
||||
@@ -31,9 +34,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v3.8.1
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
cache: 'pnpm'
|
||||
|
||||
11
.github/workflows/lucide-font.yml
vendored
@@ -9,13 +9,16 @@ on:
|
||||
- tools/build-font/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lucide-font:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -30,7 +33,7 @@ jobs:
|
||||
run: pnpm build:font
|
||||
|
||||
- name: 'Upload to Artifacts'
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: lucide-font
|
||||
path: lucide-font
|
||||
|
||||
30
.github/workflows/lucide-lab.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Lucide Lab checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/lab/**
|
||||
- packages/shared/**
|
||||
- tools/build-icons/**
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: pnpm --filter @lucide/lab build
|
||||
9
.github/workflows/lucide-preact.yml
vendored
@@ -12,13 +12,16 @@ on:
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lucide-preact:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
15
.github/workflows/lucide-react-native.yml
vendored
@@ -12,13 +12,16 @@ on:
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -32,9 +35,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
15
.github/workflows/lucide-react.yml
vendored
@@ -13,13 +13,16 @@ on:
|
||||
- scripts/generateNextJSAliases.mjs
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -33,9 +36,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
9
.github/workflows/lucide-shared.yml
vendored
@@ -9,13 +9,16 @@ on:
|
||||
- packages/shared/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
15
.github/workflows/lucide-solid.yml
vendored
@@ -12,13 +12,16 @@ on:
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -32,9 +35,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
9
.github/workflows/lucide-static.yml
vendored
@@ -7,13 +7,16 @@ on:
|
||||
- tools/build-icons/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lucide-static:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
21
.github/workflows/lucide-svelte-5.yml
vendored
@@ -12,13 +12,16 @@ on:
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -32,9 +35,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -48,9 +51,9 @@ jobs:
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
17
.github/workflows/lucide-vue.yml
vendored
@@ -6,19 +6,22 @@ on:
|
||||
- next
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/lucide-vue/**
|
||||
- packages/vue/**
|
||||
- packages/shared/**
|
||||
- tools/build-icons/**
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -32,9 +35,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
15
.github/workflows/lucide.yml
vendored
@@ -11,13 +11,16 @@ on:
|
||||
- tools/rollup-plugins/**
|
||||
- pnpm-lock.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -31,9 +34,9 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
13
.github/workflows/pull-request-icon-preview.yml
vendored
@@ -5,27 +5,30 @@ on:
|
||||
paths:
|
||||
- 'icons/*.svg'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
generate-changed-icons-comment:
|
||||
name: Generate Changed Icons Comment
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version-file: 'package.json'
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v46
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files: icons/*.svg
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
- name: Install svgson for code preview (safer and faster than installing all deps)
|
||||
run: npm install svgson@5.3.1 --force
|
||||
|
||||
@@ -40,7 +43,7 @@ jobs:
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: pr_number
|
||||
path: pr/
|
||||
|
||||
@@ -5,9 +5,12 @@ on:
|
||||
paths:
|
||||
- 'icons/*.json'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
pull-request-metadata-suggestions:
|
||||
name: Pull Request Metadata Suggestions
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -16,11 +19,11 @@ jobs:
|
||||
steps:
|
||||
# We checkout the main branch of main repository for security reasons.
|
||||
# This is to prevent the workflow from running on a forked repository.
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
repository: lucide-icons/lucide
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
|
||||
46
.github/workflows/release.yml
vendored
@@ -16,9 +16,7 @@ on:
|
||||
default: 'latest'
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: write
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
@@ -26,7 +24,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
VERSION: ${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
steps:
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
@@ -60,9 +57,9 @@ jobs:
|
||||
'@lucide/vue',
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -93,10 +90,14 @@ jobs:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: lucide-font
|
||||
path: lucide-font
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -122,10 +123,12 @@ jobs:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
needs: prepare
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
cache: 'pnpm'
|
||||
node-version-file: 'package.json'
|
||||
@@ -142,7 +145,7 @@ jobs:
|
||||
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
|
||||
|
||||
- name: 'Upload to Artifacts'
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: lucide-font
|
||||
path: lucide-font
|
||||
@@ -152,18 +155,21 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [prepare, lucide-font]
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: write
|
||||
contents: write # Required to upload the release assets
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: lucide-font
|
||||
path: lucide-font
|
||||
|
||||
- name: Zip font and icons
|
||||
run: |
|
||||
zip -r lucide-font-${{ needs.prepare.outputs.VERSION }}.zip lucide-font
|
||||
zip -r lucide-icons-${{ needs.prepare.outputs.VERSION }}.zip icons
|
||||
|
||||
- name: Release zip and fonts
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
|
||||
with:
|
||||
tag_name: ${{ needs.prepare.outputs.VERSION }}
|
||||
|
||||
|
||||
45
.github/workflows/repo-journal.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: Weekly Repo Report
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# Run this workflow every Monday at 07:00 UTC
|
||||
schedule:
|
||||
- cron: '0 7 * * 1'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
weekly-summary:
|
||||
if: github.repository == 'lucide-icons/lucide'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Node.js environment
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: Install Copilot CLI
|
||||
run: npm install -g @github/copilot
|
||||
|
||||
- name: Run the prompt
|
||||
env:
|
||||
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
|
||||
run: |
|
||||
copilot -p "${{ vars.REPO_REPORT_PROMPT }} Write the details to summary.md" --allow-tool='shell(git:*)' --allow-tool=write --no-ask-user
|
||||
cat summary.md >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Post report to Discord (private channel)
|
||||
env:
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.LUCIDE_DISCORD_WEBHOOK }}
|
||||
run: |
|
||||
jq -Rs '{
|
||||
embeds: [{
|
||||
title: "📊 Weekly Repo Report",
|
||||
description: (.[0:4096]),
|
||||
color: 5814783
|
||||
}]
|
||||
}' summary.md > payload.json
|
||||
curl -sS --fail -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @payload.json \
|
||||
"$DISCORD_WEBHOOK_URL"
|
||||
12
.github/workflows/request-review.yml
vendored
@@ -1,25 +1,27 @@
|
||||
name: 'Request Review'
|
||||
name: Request Review
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
paths:
|
||||
- icons/*.svg
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
request-review:
|
||||
if: github.event.pull_request.draft == false
|
||||
if: github.repository == 'lucide-icons/lucide' && github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v41
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files: icons/*.svg
|
||||
- run: |
|
||||
|
||||
4
.gitignore
vendored
@@ -54,8 +54,10 @@ docs/.vitepress/data/iconDetails
|
||||
docs/.vitepress/data/relatedIcons.json
|
||||
docs/.vitepress/data/brandStopwords.json
|
||||
docs/.vitepress/data/iconPopularity
|
||||
docs/.vitepress/data/lab/iconNodes
|
||||
docs/.vitepress/data/lab/iconMetaData.ts
|
||||
docs/.vitepress/data/lab/iconPopularity
|
||||
docs/.vercel
|
||||
docs/.nitro
|
||||
docs/public/og/**
|
||||
!docs/public/og/general.png
|
||||
.gitignore
|
||||
|
||||
2
.vscode/settings.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"cSpell.words": ["devs", "preact", "Preact"],
|
||||
"cSpell.words": ["devs", "linecap", "linejoin", "preact", "Preact"],
|
||||
"eslint.enable": true,
|
||||
"eslint.validate": ["javascript", "svg"],
|
||||
"svg.preview.background": "editor"
|
||||
|
||||
@@ -30,7 +30,7 @@ Read it here: [ICON_GUIDELINES](https://lucide.dev/contribute/icon-design-guide)
|
||||
|
||||
#### Lucide Studio
|
||||
|
||||
For formatting and adjusting SVG icons, [@jguddas](https://github.com/jguddas) made a great tool called [Lucide Studio](https://studio.lucide.dev/). It is a web-based SVG editor that allows you to edit and adjust icons in the Lucide style. You can use it to create new icons or modify existing ones.
|
||||
For formatting and adjusting SVG icons, [@jguddas](https://github.com/jguddas) made a great tool called [Lucide Studio](https://studio.lucide.dev/?utm_source=lucide.dev&utm_medium=contributing-guide). It is a web-based SVG editor that allows you to edit and adjust icons in the Lucide style. You can use it to create new icons or modify existing ones.
|
||||
|
||||
#### Editor guides
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@
|
||||
"kubernetes": "Kubernetes",
|
||||
"leetcode": "LeetCode",
|
||||
"leet-code": "LeetCode",
|
||||
"line": "LINE",
|
||||
"linkedin": "LinkedIn",
|
||||
"lua": "Lua",
|
||||
"mariadb": "MariaDB",
|
||||
@@ -69,7 +68,7 @@
|
||||
"nestjs": "NestJS",
|
||||
"netflix": "Netflix",
|
||||
"netlify": "Netlify",
|
||||
"next": "Next.js",
|
||||
"nextjs": "Next.js",
|
||||
"nodejs": "Node.js",
|
||||
"notion": "Notion",
|
||||
"nostr": "Nostr",
|
||||
@@ -105,7 +104,8 @@
|
||||
"shopify": "Shopify",
|
||||
"skype": "Skype",
|
||||
"slack": "Slack",
|
||||
"solid": "SolidJS",
|
||||
"snapchat": "Snapchat",
|
||||
"solidjs": "SolidJS",
|
||||
"soundcloud": "SoundCloud",
|
||||
"spotify": "Spotify",
|
||||
"sqlite": "SQLite",
|
||||
@@ -144,6 +144,5 @@
|
||||
"xbox": "Xbox",
|
||||
"yarn": "Yarn",
|
||||
"youtube": "YouTube",
|
||||
"zig": "Zig",
|
||||
"zoom": "Zoom"
|
||||
"zig": "Zig"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "../category.schema.json",
|
||||
"title": "Emoji",
|
||||
"icon": "smile"
|
||||
"icon": "face-slightly-smiling"
|
||||
}
|
||||
|
||||
25
docs/.vitepress/api/lab/icon-details/index.get.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { eventHandler, setResponseHeader } from 'h3';
|
||||
import iconMetaData from '../../../data/lab/iconMetaData';
|
||||
import iconPopularity from '../../../data/lab/iconPopularity';
|
||||
import iconNodes from '../../../data/lab/iconNodes';
|
||||
|
||||
export default eventHandler((event) => {
|
||||
setResponseHeader(event, 'Access-Control-Allow-Origin', '*');
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(iconMetaData).map(([iconName, iconData]) => {
|
||||
// Remove $schema from the response
|
||||
delete iconData['$schema'];
|
||||
|
||||
return [
|
||||
iconName,
|
||||
{
|
||||
name: iconName,
|
||||
iconNode: iconNodes[iconName],
|
||||
popularity: iconPopularity[iconName]?.count ?? 0,
|
||||
...iconData,
|
||||
},
|
||||
];
|
||||
}),
|
||||
);
|
||||
});
|
||||
14
docs/.vitepress/api/lab/icon-metadata/index.get.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { eventHandler, setResponseHeader } from 'h3';
|
||||
import iconMetaData from '../../../data/lab/iconMetaData';
|
||||
|
||||
export default eventHandler((event) => {
|
||||
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400');
|
||||
setResponseHeader(event, 'Access-Control-Allow-Origin', '*');
|
||||
|
||||
for (const iconDetail in iconMetaData) {
|
||||
// Remove $schema from the response
|
||||
delete iconMetaData[iconDetail]['$schema'];
|
||||
}
|
||||
|
||||
return iconMetaData;
|
||||
});
|
||||
30
docs/.vitepress/api/lab/icon-nodes/index.get.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { eventHandler, getQuery, setResponseHeader } from 'h3';
|
||||
import iconNodes from '../../../data/lab/iconNodes';
|
||||
type IconNodeWithKeys = [elementName: string, attrs: Record<string, string> & { key?: string }][];
|
||||
|
||||
export default eventHandler((event) => {
|
||||
const query = getQuery(event);
|
||||
|
||||
const withUniqueKeys = query.withUniqueKeys === 'true';
|
||||
|
||||
setResponseHeader(event, 'Cache-Control', 'public, max-age=86400');
|
||||
setResponseHeader(event, 'Access-Control-Allow-Origin', '*');
|
||||
|
||||
if (withUniqueKeys) {
|
||||
return iconNodes;
|
||||
}
|
||||
|
||||
return Object.entries(iconNodes).reduce((acc, [name, iconNode]) => {
|
||||
if (withUniqueKeys) {
|
||||
return [name, iconNode];
|
||||
}
|
||||
|
||||
const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs }]) => {
|
||||
return [name, attrs];
|
||||
});
|
||||
|
||||
acc[name] = newIconNode;
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
});
|
||||
@@ -101,6 +101,7 @@ export default defineConfig({
|
||||
},
|
||||
{ text: 'Packages', link: '/packages' },
|
||||
{ text: 'Showcase', link: '/showcase' },
|
||||
{ text: 'Merch', link: 'https://merch.lucide.dev/' },
|
||||
],
|
||||
sidebar,
|
||||
socialLinks: [
|
||||
|
||||
@@ -46,21 +46,5 @@
|
||||
"light": "/company-logos/open-collective-light.svg",
|
||||
"dark": "/company-logos/open-collective-dark.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Super",
|
||||
"url": "https://super.so",
|
||||
"image": {
|
||||
"light": "/company-logos/super-light.svg",
|
||||
"dark": "/company-logos/super-dark.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Noodle",
|
||||
"url": "https://noodle.run/",
|
||||
"image": {
|
||||
"light": "/company-logos/noodle-light.svg",
|
||||
"dark": "/company-logos/noodle-dark.svg"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
0
docs/.vitepress/data/lab/.gitkeep
Normal file
@@ -209,5 +209,24 @@
|
||||
],
|
||||
"source": "https://github.com/shx08/strapi-lucide-icons",
|
||||
"documentation": "https://github.com/shx08/strapi-lucide-icons/blob/main/README.md"
|
||||
},
|
||||
{
|
||||
"name": "lucide-lit",
|
||||
"description": "Implementation of Lucide icons as Lit web components.",
|
||||
"icon": "/framework-logos/lit.svg",
|
||||
"shields": [
|
||||
{
|
||||
"alt": "Latest Stable Version",
|
||||
"src": "https://img.shields.io/npm/v/@gyeonghokim/lucide-lit",
|
||||
"href": "https://www.npmjs.com/package/@gyeonghokim/lucide-lit"
|
||||
},
|
||||
{
|
||||
"alt": "Total Downloads",
|
||||
"src": "https://img.shields.io/npm/dw/@gyeonghokim/lucide-lit",
|
||||
"href": "https://www.npmjs.com/package/@gyeonghokim/lucide-lit"
|
||||
}
|
||||
],
|
||||
"source": "https://github.com/GyeongHoKim/lucide-lit",
|
||||
"documentation": "https://github.com/GyeongHoKim/lucide-lit/blob/main/README.md"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -24,13 +24,6 @@ const getHeadConfig = ({
|
||||
defer: '',
|
||||
},
|
||||
],
|
||||
[
|
||||
'script',
|
||||
{
|
||||
src: 'https://media.bitterbrains.com/main.js?from=LUCIDE&type=top',
|
||||
async: 'true',
|
||||
},
|
||||
],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
interface BackdropProps {
|
||||
src: string;
|
||||
|
||||
@@ -3,7 +3,6 @@ import pathToPoints from './path-to-points';
|
||||
import { Path, PathProps } from './types';
|
||||
|
||||
export const GapViolationHighlight = ({
|
||||
radius,
|
||||
stroke,
|
||||
strokeWidth,
|
||||
strokeOpacity,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
import { PathProps, Path } from './types';
|
||||
import getPaths, { assert } from './utils';
|
||||
import { GapViolationHighlight } from './GapViolationHighlight.tsx';
|
||||
@@ -293,7 +293,7 @@ const Radii = ({
|
||||
...props
|
||||
}: { paths: Path[] } & PathProps<
|
||||
'strokeWidth' | 'stroke' | 'strokeDasharray' | 'strokeOpacity',
|
||||
any
|
||||
never
|
||||
>) => {
|
||||
return (
|
||||
<g
|
||||
@@ -361,12 +361,12 @@ const Radii = ({
|
||||
const Handles = ({
|
||||
paths,
|
||||
...props
|
||||
}: { paths: Path[] } & PathProps<'strokeWidth' | 'stroke' | 'strokeOpacity', any>) => (
|
||||
}: { paths: Path[] } & PathProps<'strokeWidth' | 'stroke' | 'strokeOpacity', never>) => (
|
||||
<g
|
||||
className="svg-preview-handles-group"
|
||||
{...props}
|
||||
>
|
||||
{paths.map(({ c, prev, next, cp1, cp2 }, i) => (
|
||||
{paths.map(({ prev, next, cp1, cp2 }, i) => (
|
||||
<React.Fragment key={i}>
|
||||
{cp1 && <path d={`M${prev.x} ${prev.y} ${cp1.x} ${cp1.y}`} />}
|
||||
{cp1 && (
|
||||
|
||||
@@ -12,7 +12,9 @@ function pathToPoints({ d, prev, next }: Path, interval = 1) {
|
||||
points.push(commander.getPointAtLength(i));
|
||||
}
|
||||
points.push(next);
|
||||
} catch (err) {}
|
||||
} catch (err) {
|
||||
console.warn(`Failed to convert path to points for d="${d}": ${err}`);
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ export const getCommands = (src: string) =>
|
||||
.flatMap(({ d, name }, idx) =>
|
||||
new SVGPathData(d)
|
||||
.toAbs()
|
||||
// @ts-ignore
|
||||
// @ts-expect-error `commands` is typed as a union of command types svgson does not narrow
|
||||
.commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })),
|
||||
);
|
||||
|
||||
@@ -242,7 +242,7 @@ const getPaths = (src: string) => {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error every command type is handled above, so `c` is not `never` here
|
||||
assertNever(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ const getIconCodes = (): CodeExampleType => {
|
||||
title: 'Vanilla',
|
||||
code: `\
|
||||
<script>
|
||||
import { createIcons, $CamelCase } from 'lucide';
|
||||
import { createIcons, $PascalCase } from 'lucide';
|
||||
|
||||
createIcons({
|
||||
icons: {
|
||||
$CamelCase
|
||||
$PascalCase
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -9,11 +9,9 @@ export const hash = (string: string, seed = 5381) => {
|
||||
let i = string.length;
|
||||
|
||||
while (i) {
|
||||
// eslint-disable-next-line no-bitwise, no-plusplus
|
||||
seed = (seed * 33) ^ string.charCodeAt(--i);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-bitwise
|
||||
return (seed >>> 0).toString(36).substr(0, 6);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type MarkdownIt from 'markdown-it';
|
||||
import type { RenderRule } from 'markdown-it/lib/renderer.mjs';
|
||||
import type Token from 'markdown-it/lib/token.mjs';
|
||||
import container from 'markdown-it-container';
|
||||
import sandpackTheme from '../theme/sandpackTheme.json';
|
||||
|
||||
@@ -21,14 +22,10 @@ export default function sandpackPlugin(md: MarkdownIt, pluginOptions: SnackParam
|
||||
throw new Error('MarkdownIt instance is required for sandpackPlugin');
|
||||
}
|
||||
const escapeHtml = md?.utils?.escapeHtml;
|
||||
const defaultFence =
|
||||
md.renderer.rules.fence ||
|
||||
((tokens, idx, options, env, self) => self.renderToken(tokens, idx, options));
|
||||
|
||||
const renderSandbox = (tokenList: any[], index: number) => {
|
||||
const renderFunc = (tokens: any[], idx: number) => {
|
||||
const renderSandbox = (tokenList: Token[], index: number) => {
|
||||
const renderFunc = (tokens: Token[], idx: number) => {
|
||||
if (tokens[idx].nesting === 1) {
|
||||
const fileAttr: string[] = [];
|
||||
const attrs = Object.fromEntries(tokens[idx].attrs || []);
|
||||
|
||||
const files: Record<
|
||||
@@ -47,7 +44,7 @@ export default function sandpackPlugin(md: MarkdownIt, pluginOptions: SnackParam
|
||||
) {
|
||||
if (tokens[i].type === 'fence' && tokens[i].tag === 'code') {
|
||||
const info = tokens[i].info ?? '';
|
||||
const [lang, fileName, params = ''] = info.split(' ');
|
||||
const [, fileName, params = ''] = info.split(' ');
|
||||
|
||||
const active = params.includes('[active]');
|
||||
const hidden = params.includes('[hidden]');
|
||||
@@ -57,15 +54,9 @@ export default function sandpackPlugin(md: MarkdownIt, pluginOptions: SnackParam
|
||||
if (fileName && code) {
|
||||
files[fileName] = {
|
||||
code,
|
||||
...(active && { active: true }),
|
||||
...(hidden && { hidden: true }),
|
||||
};
|
||||
|
||||
if (active) {
|
||||
(files[fileName] as any).active = true;
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
(files[fileName] as any).hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +113,7 @@ export default function sandpackPlugin(md: MarkdownIt, pluginOptions: SnackParam
|
||||
return renderFunc(tokenList, index);
|
||||
};
|
||||
|
||||
function createCodeGroup(md: MarkdownIt): ContainerArgs {
|
||||
function createCodeGroup(): ContainerArgs {
|
||||
return [
|
||||
container,
|
||||
'sandpack',
|
||||
@@ -134,5 +125,5 @@ export default function sandpackPlugin(md: MarkdownIt, pluginOptions: SnackParam
|
||||
];
|
||||
}
|
||||
|
||||
md.use(...createCodeGroup(md));
|
||||
md.use(...createCodeGroup());
|
||||
}
|
||||
|
||||
@@ -73,7 +73,6 @@ export default function snackPlayerPlugin(md: MarkdownIt) {
|
||||
'react-native-safe-area-context' + (params.dependencies ? `,${params.dependencies}` : '');
|
||||
const platform = params.platform ?? 'web';
|
||||
const supportedPlatforms = params.supportedPlatforms ?? 'ios,android,web';
|
||||
const theme = params.theme ?? 'light';
|
||||
const preview = params.preview ?? 'true';
|
||||
const loading = params.loading ?? 'lazy';
|
||||
const deviceAppearance = params.deviceAppearance ?? 'dark';
|
||||
@@ -88,7 +87,7 @@ export default function snackPlayerPlugin(md: MarkdownIt) {
|
||||
` data-snack-dependencies="${escapeHtml(dependencies)}"` +
|
||||
` data-snack-platform="${escapeHtml(platform)}"` +
|
||||
` data-snack-supported-platforms="${escapeHtml(supportedPlatforms)}"` +
|
||||
// ` data-snack-theme="${escapeHtml(theme)}"` +
|
||||
// ` data-snack-theme="${escapeHtml(params.theme ?? 'light')}"` +
|
||||
` data-snack-preview="${escapeHtml(preview)}"` +
|
||||
` data-snack-loading="${escapeHtml(loading)}"` +
|
||||
` data-snack-device-appearance="${escapeHtml(deviceAppearance)}"` +
|
||||
|
||||
@@ -72,6 +72,11 @@ export const reactNativeSidebar = [
|
||||
link: '/guide/react-native/advanced/combining-icons',
|
||||
desc: 'Combine multiple icons into one',
|
||||
},
|
||||
{
|
||||
text: 'Optimizations',
|
||||
link: '/guide/react-native/advanced/optimizations',
|
||||
desc: 'Optimizing your app for performance',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vitepress';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const frameworks = [
|
||||
{
|
||||
ariaLabel: 'Get JavaScript certificates from certificates.dev',
|
||||
image: '/certificates-dev/js-certificates-dev.jpg',
|
||||
href: 'https://certificates.dev/javascript?friend=LUCIDE',
|
||||
routes: [
|
||||
'/guide/lucide/',
|
||||
'/guide/svelte/',
|
||||
'/guide/solid/',
|
||||
'/guide/astro/',
|
||||
'/guide/static/'
|
||||
],
|
||||
},
|
||||
{
|
||||
ariaLabel: 'Get React certificates from certificates.dev',
|
||||
image: '/certificates-dev/react-certificates-dev.jpg',
|
||||
href: 'https://certificates.dev/react?friend=LUCIDE',
|
||||
routes: [
|
||||
'/guide/react/',
|
||||
'/guide/react-native/',
|
||||
'/guide/preact/',
|
||||
],
|
||||
},
|
||||
{
|
||||
ariaLabel: 'Get Vue certificates from certificates.dev',
|
||||
href: 'https://certificates.dev/vuejs?friend=LUCIDE',
|
||||
image: '/certificates-dev/vue-certificates-dev.jpg',
|
||||
routes: [
|
||||
'/guide/vue/',
|
||||
],
|
||||
},
|
||||
{
|
||||
ariaLabel: 'Get Angular certificates from certificates.dev',
|
||||
href: 'https://certificates.dev/angular?friend=LUCIDE',
|
||||
image: '/certificates-dev/angular-certificates-dev.jpg',
|
||||
routes: [
|
||||
'/guide/angular/',
|
||||
],
|
||||
}
|
||||
];
|
||||
|
||||
const selected = computed(() => {
|
||||
const current = frameworks.find(({ routes }) => {
|
||||
return routes.some(route => router.route.path.startsWith(route));
|
||||
});
|
||||
|
||||
return current || frameworks[0];
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a class="certificates-link" :href="selected.href" :aria-label="selected.ariaLabel" target="_blank" rel="noopener noreferrer">
|
||||
<img :src="selected.image" :alt="selected.ariaLabel" />
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.certificates-link {
|
||||
/* padding: 20px; */
|
||||
border-radius: 12px;
|
||||
min-height: 100px;
|
||||
text-align: center;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
background-color: var(--vp-carbon-ads-bg-color);
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.certificates-link img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
border-radius: 6px;
|
||||
transition: transform 0.3s ease, opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.certificates-link:hover img {
|
||||
transform: scale(1.05);
|
||||
opacity: 0.9;
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -13,23 +13,21 @@ const { go } = useRouter()
|
||||
<HomeSectionTitle>Available For:</HomeSectionTitle>
|
||||
<div class="packages-list">
|
||||
<a
|
||||
v-for="{ name, logo, logoDark } in data.packages"
|
||||
:href="`/guide/packages/${name}`"
|
||||
v-for="{ name, label, path, logo, logoDark } in data.packages"
|
||||
:href="path"
|
||||
class="package-logo"
|
||||
:aria-label="`Read more about: ${name} package`"
|
||||
@click.prevent="go(`/guide/packages/${name}`)"
|
||||
@click.prevent="go(path)"
|
||||
>
|
||||
<img
|
||||
:src="logo"
|
||||
:class="{ light: logoDark, 'image-logo': true }"
|
||||
:alt="`${name} logo`"
|
||||
:alt="label"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
<img
|
||||
v-if="logoDark"
|
||||
:src="logoDark"
|
||||
:alt="`${name} logo`"
|
||||
:alt="label"
|
||||
class="image-logo dark"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getAllData } from '../../../lib/icons';
|
||||
import { getAllCategoryFiles, mapCategoryIconCount } from '../../../lib/categories';
|
||||
import iconsMetaData from '../../../data/iconMetaData';
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { IconEntity } from '../../types';
|
||||
import { computed, ref } from 'vue';
|
||||
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
|
||||
import { useIconStyleContext } from '../../composables/useIconStyle';
|
||||
import { squarePen } from '../../../data/iconNodes';
|
||||
|
||||
const props = defineProps<{
|
||||
name: IconEntity['name'];
|
||||
@@ -21,6 +22,31 @@ const iconComponent = computed(() => {
|
||||
if (!props.name || !props.iconNode) return null;
|
||||
return createLucideIcon(props.name, props.iconNode);
|
||||
});
|
||||
|
||||
const SquarePenIcon = createLucideIcon('SquarePen', squarePen);
|
||||
|
||||
const innerSvg = computed(() => {
|
||||
if (!props.iconNode) return '';
|
||||
|
||||
const string = props.iconNode
|
||||
.map(([tag, attrs]) => {
|
||||
const attributes = Object.entries(attrs)
|
||||
.map(([key, value]) => `${key}="${value}"`)
|
||||
.join(' ');
|
||||
return `<${tag} ${attributes}/>`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
return string
|
||||
.replace(/>[\r\n ]+</g, '><')
|
||||
.replace(/(<.*?>)|\s+/g, (m, $1) => $1 || ' ')
|
||||
.trim();
|
||||
});
|
||||
|
||||
const base64InnerSvg = computed(() => {
|
||||
if (typeof window === 'undefined') return '';
|
||||
return btoa(innerSvg.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -73,6 +99,16 @@ const iconComponent = computed(() => {
|
||||
100%
|
||||
</button>
|
||||
</div>
|
||||
<a
|
||||
:href="`https://studio.lucide.dev/edit?value=${encodeURIComponent(base64InnerSvg)}&name=${name}&utm_source=lucide.dev&utm_medium=icon-detail-preview`"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="studio-button"
|
||||
title="Open in Lucide Studio"
|
||||
aria-label="Open in Lucide Studio"
|
||||
>
|
||||
<SquarePenIcon :size="14" />
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -99,6 +135,8 @@ const iconComponent = computed(() => {
|
||||
background: var(--vp-c-bg-alt);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.icon-container:hover .size-buttons {
|
||||
@@ -131,6 +169,34 @@ const iconComponent = computed(() => {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.studio-button {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
right: 8px;
|
||||
background: var(--vp-c-bg);
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--vp-c-text-2);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s, color 0.2s, background-color 0.2s;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.icon-container:hover .studio-button,
|
||||
.studio-button:focus-visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.studio-button:hover,
|
||||
.studio-button:focus-visible {
|
||||
background: var(--vp-c-bg-soft);
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
.icon-component.customizable {
|
||||
will-change: width, height, stroke-width, stroke;
|
||||
}
|
||||
|
||||
@@ -44,8 +44,12 @@ function setActiveIconName(name: string) {
|
||||
activeIconName.value = name;
|
||||
}
|
||||
|
||||
const { execute: fetchTags, data: tags } = useFetchTags();
|
||||
const { execute: fetchCategories, data: categoriesMap } = useFetchCategories();
|
||||
const { execute: fetchTags, data: tags, isFetching: isFetchingTags } = useFetchTags();
|
||||
const {
|
||||
execute: fetchCategories,
|
||||
data: categoriesMap,
|
||||
isFetching: isFetchingCategories,
|
||||
} = useFetchCategories();
|
||||
|
||||
const overviewEl = ref<HTMLElement | null>(null);
|
||||
const { width: containerWidth } = useElementSize(overviewEl);
|
||||
@@ -117,6 +121,9 @@ const categoriesList = computed(() => {
|
||||
}, []);
|
||||
});
|
||||
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
|
||||
const isSearchMetadataLoading = computed(
|
||||
() => searchQuery.value.length > 0 && (tags.value == null || categoriesMap.value == null),
|
||||
);
|
||||
|
||||
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(categoriesList, {
|
||||
itemHeight: ICON_SIZE + ICON_GRID_GAP,
|
||||
@@ -135,15 +142,21 @@ onMounted(() => {
|
||||
useEventListener(window, 'scroll', containerProps.onScroll);
|
||||
});
|
||||
|
||||
function onFocusSearchInput() {
|
||||
if (tags.value == null) {
|
||||
fetchTags();
|
||||
function loadSearchMetadata() {
|
||||
if (tags.value == null && !isFetchingTags.value) {
|
||||
void fetchTags();
|
||||
}
|
||||
if (categoriesMap.value == null) {
|
||||
fetchCategories();
|
||||
if (categoriesMap.value == null && !isFetchingCategories.value) {
|
||||
void fetchCategories();
|
||||
}
|
||||
}
|
||||
|
||||
watch(searchQuery, (searchString) => {
|
||||
if (searchString !== '') {
|
||||
loadSearchMetadata();
|
||||
}
|
||||
});
|
||||
|
||||
const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
|
||||
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
|
||||
|
||||
@@ -178,11 +191,11 @@ function handleCloseDrawer() {
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="input-wrapper"
|
||||
ref="searchInput"
|
||||
@focus="onFocusSearchInput"
|
||||
@focus="loadSearchMetadata"
|
||||
/>
|
||||
</StickyBar>
|
||||
<NoResults
|
||||
v-if="searchPlaceholder.isNoResults"
|
||||
v-if="searchPlaceholder.isNoResults && !isSearchMetadataLoading"
|
||||
:searchQuery="searchPlaceholder.query"
|
||||
:isBrandSearch="searchPlaceholder.isBrand"
|
||||
@clear="searchQuery = ''"
|
||||
|
||||
@@ -51,8 +51,12 @@ const props = defineProps<{
|
||||
const activeIconName = ref(null);
|
||||
const selectedSort = ref(SORTING[0])
|
||||
|
||||
const { execute: fetchTags, data: tags } = useFetchTags();
|
||||
const { execute: fetchCategories, data: categories } = useFetchCategories();
|
||||
const { execute: fetchTags, data: tags, isFetching: isFetchingTags } = useFetchTags();
|
||||
const {
|
||||
execute: fetchCategories,
|
||||
data: categories,
|
||||
isFetching: isFetchingCategories,
|
||||
} = useFetchCategories();
|
||||
|
||||
const overviewEl = ref<HTMLElement | null>(null);
|
||||
const { width: containerWidth } = useElementSize(overviewEl);
|
||||
@@ -109,6 +113,9 @@ const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
|
||||
]);
|
||||
|
||||
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
|
||||
const isSearchMetadataLoading = computed(
|
||||
() => searchQuery.value.length > 0 && (tags.value == null || categories.value == null),
|
||||
);
|
||||
|
||||
const chunkedIcons = computed(() => {
|
||||
return chunkArray(searchResults.value, columnSize.value);
|
||||
@@ -134,15 +141,21 @@ function setActiveIconName(name: string) {
|
||||
activeIconName.value = name;
|
||||
}
|
||||
|
||||
function onFocusSearchInput() {
|
||||
if (tags.value == null) {
|
||||
fetchTags();
|
||||
function loadSearchMetadata() {
|
||||
if (tags.value == null && !isFetchingTags.value) {
|
||||
void fetchTags();
|
||||
}
|
||||
if (categories.value == null) {
|
||||
fetchCategories();
|
||||
if (categories.value == null && !isFetchingCategories.value) {
|
||||
void fetchCategories();
|
||||
}
|
||||
}
|
||||
|
||||
watch(searchQuery, (searchString) => {
|
||||
if (searchString !== '') {
|
||||
loadSearchMetadata();
|
||||
}
|
||||
});
|
||||
|
||||
const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
|
||||
|
||||
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
|
||||
@@ -178,7 +191,7 @@ function handleCloseDrawer() {
|
||||
ref="searchInput"
|
||||
:shortcut="kbdSearchShortcut"
|
||||
class="input-wrapper"
|
||||
@focus="onFocusSearchInput"
|
||||
@focus="loadSearchMetadata"
|
||||
/>
|
||||
|
||||
<Select
|
||||
@@ -196,7 +209,7 @@ function handleCloseDrawer() {
|
||||
</Select>
|
||||
</StickyBar>
|
||||
<NoResults
|
||||
v-if="searchPlaceholder.isNoResults"
|
||||
v-if="searchPlaceholder.isNoResults && !isSearchMetadataLoading"
|
||||
:searchQuery="searchPlaceholder.query"
|
||||
:isBrandSearch="searchPlaceholder.isBrand"
|
||||
@clear="searchQuery = ''"
|
||||
|
||||
@@ -16,7 +16,7 @@ export const externalLibContext = {
|
||||
};
|
||||
|
||||
const externalLibIconNodesAPI = {
|
||||
lab: 'https://lab.lucide.dev/api/icon-details',
|
||||
lab: `${import.meta.env.DEV ? 'http://localhost:3000' : ''}/api/lab/icon-details`,
|
||||
};
|
||||
|
||||
export function useExternalLibs(): ExternalLibContext {
|
||||
@@ -32,13 +32,15 @@ export function useExternalLibs(): ExternalLibContext {
|
||||
newExternalIconNodes[lib] = savedIconNodes[lib];
|
||||
} else {
|
||||
const response = await fetch(externalLibIconNodesAPI[lib]);
|
||||
const iconNodes = await response.json();
|
||||
const iconDetails = await response.json();
|
||||
|
||||
if (iconNodes) {
|
||||
newExternalIconNodes[lib] = Object.values(iconNodes).map((iconEntity: IconEntity) => ({
|
||||
...iconEntity,
|
||||
externalLibrary: lib,
|
||||
}));
|
||||
if (iconDetails) {
|
||||
newExternalIconNodes[lib] = Object.values(iconDetails).map((iconEntity: IconEntity) => {
|
||||
return {
|
||||
...iconEntity,
|
||||
externalLibrary: lib,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import { ref, inject, Ref } from 'vue';
|
||||
|
||||
export const ICON_STYLE_CONTEXT = Symbol('style');
|
||||
|
||||
@@ -12,7 +12,6 @@ import { CATEGORY_VIEW_CONTEXT, categoryViewContext } from './composables/useCat
|
||||
import { EXTERNAL_LIBS_CONTEXT, externalLibContext } from './composables/useExternalLibs';
|
||||
import FrameworkSelect from './components/guide/FrameworkSelect.vue';
|
||||
import SnackPlayer from './components/editors/SnackPlayer.vue';
|
||||
import CertificatesLink from './components/guide/CertificatesLink.vue';
|
||||
|
||||
const theme: Partial<Theme> = {
|
||||
extends: DefaultTheme,
|
||||
@@ -23,7 +22,6 @@ const theme: Partial<Theme> = {
|
||||
'sidebar-nav-after': () => h(IconsSidebarNavAfter),
|
||||
'home-hero-image': () => h(HomeHeroIconsCard),
|
||||
'home-hero-actions-after': () => h(HomeHeroAfter),
|
||||
'aside-ads-before': () => h(CertificatesLink),
|
||||
// Keep this here for now, we might want to add more things to the top bar in the future
|
||||
// 'layout-top': () => h(LayoutTop),
|
||||
});
|
||||
|
||||
@@ -67,6 +67,10 @@
|
||||
animation: .3s transition 0s ease;
|
||||
} */
|
||||
|
||||
html {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.VPHome {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
@@ -148,14 +152,6 @@
|
||||
.VPHomeHero .container {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
html.has-bb-banner {
|
||||
--vp-layout-top-height: 72px;
|
||||
}
|
||||
|
||||
html.has-bb-banner {
|
||||
--Dark-Background: #161618;
|
||||
}
|
||||
}
|
||||
|
||||
.VPNavBarHamburger .container > span {
|
||||
@@ -167,12 +163,6 @@
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
html:not(.has-bb-banner) #bb-banner {
|
||||
display: none !important;
|
||||
visibility: hidden !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
.VPSidebar {
|
||||
/* Hide Scrollbar */
|
||||
-ms-overflow-style: none;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PageData } from 'vitepress';
|
||||
import { type PageData } from 'vitepress';
|
||||
import getStructuredData from './getStructuredData';
|
||||
import { createGeneralOGImage, createIconOGImage } from './createOGImage';
|
||||
|
||||
|
||||
6
docs/.vitepress/types.d.ts
vendored
@@ -6,13 +6,13 @@ declare module '*.vue' {
|
||||
}
|
||||
|
||||
declare module '*.data.ts' {
|
||||
const data: any;
|
||||
const data: unknown;
|
||||
|
||||
export { data };
|
||||
}
|
||||
|
||||
declare module '*.data' {
|
||||
const data: any;
|
||||
const data: unknown;
|
||||
|
||||
export { data };
|
||||
}
|
||||
@@ -75,7 +75,7 @@ declare global {
|
||||
doNotShowAfterSubmit?: boolean;
|
||||
customFormUrl?: string; // when you want to load the form via it's custom domain URL
|
||||
hiddenFields?: {
|
||||
[key: string]: any;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
onOpen?: () => void;
|
||||
onClose?: () => void;
|
||||
|
||||
@@ -158,7 +158,7 @@ For each icon these attributes are applied, corresponding to the above rules.
|
||||
### Minify paths
|
||||
|
||||
The code of paths can sometimes get quite large. To reduce file size we like to minify the code.
|
||||
We recommend to use [Lucide Studio](https://studio.lucide.dev/) to tidy paths to 3 points of precision.
|
||||
We recommend to use [Lucide Studio](https://studio.lucide.dev/?utm_source=lucide.dev&utm_medium=design-guide) to tidy paths to 3 points of precision.
|
||||
|
||||
### Allowed elements
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@ import Sandpack from '~/.vitepress/theme/components/editors/SandpackAngular.vue'
|
||||
|
||||
While they aren't provided as standalone components, they can be still be passed to the `LucideIcon` component the same way as official icons:
|
||||
|
||||
::: info Limitation
|
||||
Only custom icons that follow Lucide's [code conventions](../../../contribute/icon-design-guide.md#code-conventions) are supported.
|
||||
:::
|
||||
|
||||
### Directly as LucideIconData
|
||||
|
||||
::: sandpack {template=angular showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="@lucide/angular,@lucide/lab"}
|
||||
|
||||
56
docs/guide/react-native/advanced/optimizations.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: Optimizations - React Native
|
||||
description: Learn how to optimize the bundle size of your React Native and Expo web exports by tree-shaking Lucide icons.
|
||||
---
|
||||
|
||||
# Optimizations
|
||||
|
||||
By default you import icons from the `lucide-react-native` entry point:
|
||||
|
||||
```jsx
|
||||
import { Camera } from 'lucide-react-native';
|
||||
```
|
||||
|
||||
This is convenient, but it relies on the bundler to _tree-shake_ the entry point (a "barrel" file that re-exports every icon) so that only the icons you actually use end up in your bundle.
|
||||
|
||||
On native platforms this is usually fine. On **web exports** it can be a problem, because the Metro bundler is currently not very good at tree-shaking barrel imports and may include **all** icons in your final bundle.
|
||||
|
||||
## The Metro tree-shaking limitation
|
||||
|
||||
When you build for web with Expo (`expo export --platform web`), Metro is used to bundle your JavaScript. Metro bundles modules on-demand and, by default, does not remove unused exports from a barrel file.
|
||||
|
||||
This means an import like the one below can pull in the entire icon set, even though only a single icon is rendered:
|
||||
|
||||
```jsx
|
||||
// May bundle every Lucide icon on web exports
|
||||
import { Camera } from 'lucide-react-native';
|
||||
```
|
||||
|
||||
Expo documents an experimental optimization for [React Native web imports](https://docs.expo.dev/guides/tree-shaking/#react-native-web-imports) and for [removing unused imports and exports](https://docs.expo.dev/guides/tree-shaking/#remove-unused-imports-and-exports). In practice this tree-shaking does **not** currently work reliably for `lucide-react-native` barrel imports, so the unused icons are not removed from web exports.
|
||||
|
||||
## Recommended: import icons individually
|
||||
|
||||
To guarantee that only the icons you use are bundled — regardless of the bundler's tree-shaking capabilities — import each icon directly from its own module:
|
||||
|
||||
```jsx
|
||||
import Camera from 'lucide-react-native/icons/camera';
|
||||
|
||||
// Usage
|
||||
const App = () => {
|
||||
return <Camera />;
|
||||
};
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
Because each icon lives in its own file, the bundler only ever includes the icons you explicitly import. This keeps your web export small without depending on Metro's experimental tree-shaking.
|
||||
|
||||
The icon module name is the [kebab-case](https://developer.mozilla.org/en-US/docs/Glossary/Kebab_case) version of the icon name. For example, the `ArrowRight` icon is imported from `lucide-react-native/icons/arrow-right`:
|
||||
|
||||
```jsx
|
||||
import ArrowRight from 'lucide-react-native/icons/arrow-right';
|
||||
```
|
||||
|
||||
::: tip
|
||||
Individual icon imports work identically on native and web, so you can safely use this style across your whole project to keep bundles as small as possible on every platform.
|
||||
:::
|
||||
@@ -1,24 +1,26 @@
|
||||
import { IconEntity } from '../../.vitepress/theme/types';
|
||||
import iconNodes from '../../.vitepress/data/lab/iconNodes';
|
||||
import iconPopularity from '../../.vitepress/data/lab/iconPopularity';
|
||||
import iconMetaData from '../../.vitepress/data/lab/iconMetaData';
|
||||
import { type IconEntity } from '../../.vitepress/theme/types';
|
||||
|
||||
export default {
|
||||
paths: async () => {
|
||||
try {
|
||||
const iconDetailsResponse = await fetch('https://lab.lucide.dev/api/icon-details');
|
||||
const iconDetails = (await iconDetailsResponse.json()) as Record<string, IconEntity>;
|
||||
return (Object.entries(iconNodes) as unknown as IconEntity[]).map(([name, iconNode]) => {
|
||||
const iconDetails = iconMetaData[name];
|
||||
|
||||
return Object.values(iconDetails).map((iconEntity) => {
|
||||
const params = {
|
||||
externalLibrary: 'lab',
|
||||
...iconEntity,
|
||||
};
|
||||
delete iconDetails['$schema'];
|
||||
|
||||
return {
|
||||
params,
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching icon details:', error);
|
||||
return [];
|
||||
}
|
||||
const params = {
|
||||
name,
|
||||
iconNode,
|
||||
externalLibrary: 'lab',
|
||||
popularity: iconPopularity[name]?.count ?? 0,
|
||||
...iconDetails,
|
||||
};
|
||||
|
||||
return {
|
||||
params,
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -8,19 +8,21 @@
|
||||
"docs:dev": "pnpm run /^prebuild:.*/ && vitepress dev",
|
||||
"docs:build": "pnpm run /^prebuild:.*/ && vitepress build",
|
||||
"docs:preview": "vitepress preview",
|
||||
"build:docs": "vitepress build",
|
||||
"build:docs": "NODE_OPTIONS=--max-old-space-size=8192 vitepress build",
|
||||
"prebuild:iconNodes": "node ./scripts/writeIconNodes.mts",
|
||||
"prebuild:metaJson": "node ./scripts/writeIconMetaIndex.mts",
|
||||
"prebuild:releaseJson": "node ./scripts/writeReleaseMetadata.mts",
|
||||
"prebuild:categoriesJson": "node ./scripts/writeCategoriesMetadata.mts",
|
||||
"prebuild:relatedIcons": "node ./scripts/writeIconRelatedIcons.mts",
|
||||
"prebuild:iconDetails": "node ./scripts/writeIconDetails.mts",
|
||||
"prebuild:iconPopularity": "node ./scripts/writeiconPopularity.mts",
|
||||
"prebuild:iconPopularity": "node ./scripts/writeiconPopularity.mts && node ./scripts/writeiconPopularityIndex.mts",
|
||||
"prebuild:brandStopwords": "node ./scripts/writeBrandStopwords.mts",
|
||||
"prebuild:writeLabIconNodes": "node ./scripts/writeLabIconNodes.mts",
|
||||
"prebuild:writeLabIconMetaIndex": "node ./scripts/writeLabIconMetaIndex.mts",
|
||||
"postbuild:vercelJson": "node ./scripts/writeVercelOutput.mts",
|
||||
"dev": "npx nitropack dev",
|
||||
"prebuild:api": "npx nitropack prepare",
|
||||
"build:api": "npx nitropack build",
|
||||
"dev": "nitropack dev",
|
||||
"prebuild:api": "nitropack prepare",
|
||||
"build:api": "NODE_OPTIONS=--max-old-space-size=8192 nitropack build",
|
||||
"build": "pnpm run /^prebuild:.*/ && pnpm run build:api && pnpm run build:docs && pnpm postbuild:vercelJson",
|
||||
"preview": "node .output/server/index.mjs"
|
||||
},
|
||||
@@ -32,31 +34,32 @@
|
||||
"@lucide/shared": "workspace:*",
|
||||
"@rollup/plugin-replace": "^6.0.3",
|
||||
"@types/semver": "^7.7.1",
|
||||
"h3": "^1.15.9",
|
||||
"h3": "^1.15.11",
|
||||
"markdown-it": "^14.3.0",
|
||||
"markdown-it-container": "^4.0.0",
|
||||
"nitropack": "^2.13.4",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"svg-path-commander": "^2.1.11",
|
||||
"vitepress": "^1.6.4",
|
||||
"vitepress-plugin-group-icons": "^1.6.5",
|
||||
"vitepress-plugin-llms": "^1.11.1",
|
||||
"markdown-it": "^14.2.0",
|
||||
"markdown-it-container": "^4.0.0",
|
||||
"semver": "^7.7.3",
|
||||
"semver": "^7.8.5",
|
||||
"sharp": "^0.34.5",
|
||||
"shiki": "^3.15.0",
|
||||
"shiki": "^3.23.0",
|
||||
"simple-git": "^3.36.0",
|
||||
"sitemap": "^7.1.2",
|
||||
"sitemap": "^7.1.3",
|
||||
"svg-path-commander": "^2.2.1",
|
||||
"svg-pathdata": "^6.0.3",
|
||||
"svgson": "^5.3.1"
|
||||
"svgson": "^5.3.1",
|
||||
"vitepress": "^1.6.4",
|
||||
"vitepress-plugin-group-icons": "^1.7.5",
|
||||
"vitepress-plugin-llms": "^1.13.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/vue": "^1.1.9",
|
||||
"@floating-ui/vue": "^1.1.11",
|
||||
"@headlessui/vue": "^1.7.23",
|
||||
"@lucide/vue": "workspace:*",
|
||||
"@vueuse/components": "^14.0.0",
|
||||
"@vueuse/core": "^14.0.0",
|
||||
"@vueuse/components": "^14.3.0",
|
||||
"@vueuse/core": "^14.3.0",
|
||||
"@vueuse/integrations": "^14.3.0",
|
||||
"element-to-path": "^1.2.1",
|
||||
"fuse.js": "^7.1.0",
|
||||
"fuse.js": "^7.5.0",
|
||||
"jszip": "^3.10.1",
|
||||
"lodash-es": "^4.18.1",
|
||||
"lucide": "workspace:*",
|
||||
@@ -65,6 +68,6 @@
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"sandpack-vue3": "3.1.12",
|
||||
"vue": "^3.5.24"
|
||||
"vue": "^3.5.41"
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 79 KiB |
@@ -1,28 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="10 -10 304 120">
|
||||
<g clip-path="url(#a)">
|
||||
<g clip-path="url(#b)">
|
||||
<path fill="url(#c)" d="M72 0H28A28 28 0 0 0 0 28v44a28 28 0 0 0 28 28h44a28 28 0 0 0 28-28V28A28 28 0 0 0 72 0Z"/>
|
||||
<path stroke="#161616" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="m47.81 54.34 26.63-6.66-19.73-19.1-26.63 6.67 19.73 19.1Z"/>
|
||||
<path stroke="#161616" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="m47.81 54.34 18.23-4.56c.67 5.86-.29 11.8-2.76 17.15a31.87 31.87 0 0 0-19.65 3.01 31.87 31.87 0 0 0-15.5-12.43 32.2 32.2 0 0 1 6.18-16.23l13.5 13.06Z"/>
|
||||
<path stroke="#161616" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="m33.37 67.04 5.17-19.32 11.84-2.96m-2.57 9.58 26.63-6.66-19.73-19.1-26.63 6.67 19.73 19.1Zm0 0 18.23-4.56c.67 5.86-.29 11.8-2.76 17.15a31.87 31.87 0 0 0-19.65 3.01 31.87 31.87 0 0 0-15.5-12.43 32.2 32.2 0 0 1 6.18-16.23l13.5 13.06Z"/>
|
||||
</g>
|
||||
<path fill="#161616" d="M281.5 67.51c-2.63 0-4.9-.54-6.8-1.64a11.08 11.08 0 0 1-4.39-4.67 15.64 15.64 0 0 1-1.53-7.14c0-2.7.51-5.05 1.53-7.09a11.6 11.6 0 0 1 4.33-4.77 12.14 12.14 0 0 1 6.53-1.72c1.62 0 3.14.26 4.57.78 1.44.51 2.72 1.3 3.82 2.39a11.18 11.18 0 0 1 2.62 4.12c.64 1.66.96 3.64.96 5.93v1.9h-21.46v-4.16h15.54a6.63 6.63 0 0 0-.76-3.16 5.58 5.58 0 0 0-2.1-2.2 5.9 5.9 0 0 0-3.1-.8c-1.26 0-2.37.31-3.33.92-.95.6-1.7 1.4-2.23 2.4a6.88 6.88 0 0 0-.8 3.21v3.63c0 1.53.28 2.83.83 3.92a5.96 5.96 0 0 0 2.34 2.5c1 .56 2.17.84 3.51.84.9 0 1.71-.12 2.44-.37a5.4 5.4 0 0 0 1.89-1.14c.53-.5.94-1.12 1.21-1.86l5.76.65a9 9 0 0 1-2.08 3.99c-1 1.12-2.3 2-3.88 2.62-1.58.61-3.39.92-5.42.92Z"/>
|
||||
<path fill="#161616" d="M263.56 32.1V67h-6.17V32.1h6.17Z"/>
|
||||
<path fill="#161616" d="M236.87 67.46a9.9 9.9 0 0 1-5.52-1.58 10.77 10.77 0 0 1-3.85-4.6 17.18 17.18 0 0 1-1.42-7.34c0-2.9.48-5.36 1.43-7.36a10.6 10.6 0 0 1 3.9-4.55 10.03 10.03 0 0 1 5.48-1.55c1.53 0 2.8.26 3.78.78a7.13 7.13 0 0 1 2.35 1.86c.58.72 1.03 1.4 1.35 2.03h.26V32.09h6.18V67h-6.06v-4.13h-.38c-.32.64-.78 1.32-1.38 2.03a7.6 7.6 0 0 1-6.12 2.56Zm1.72-5.06c1.3 0 2.42-.35 3.34-1.06a6.6 6.6 0 0 0 2.1-2.98c.48-1.27.72-2.76.72-4.45 0-1.7-.24-3.17-.72-4.42a6.28 6.28 0 0 0-2.08-2.91 5.38 5.38 0 0 0-3.36-1.04c-1.37 0-2.52.36-3.44 1.07a6.5 6.5 0 0 0-2.08 2.97c-.47 1.26-.7 2.7-.7 4.33 0 1.64.23 3.1.7 4.38a6.84 6.84 0 0 0 2.1 3.02 5.4 5.4 0 0 0 3.42 1.09Z"/>
|
||||
<path fill="#161616" d="M209.37 67.51c-2.56 0-4.77-.56-6.65-1.69a11.46 11.46 0 0 1-4.36-4.72 15.52 15.52 0 0 1-1.53-7.09c0-2.7.5-5.07 1.53-7.1a11.43 11.43 0 0 1 4.36-4.75 12.68 12.68 0 0 1 6.65-1.68c2.56 0 4.78.56 6.65 1.68a11.32 11.32 0 0 1 4.35 4.74 15.47 15.47 0 0 1 1.55 7.11c0 2.7-.52 5.07-1.55 7.1a11.34 11.34 0 0 1-4.35 4.71 12.68 12.68 0 0 1-6.65 1.7Zm.04-4.94c1.38 0 2.54-.38 3.47-1.14a6.89 6.89 0 0 0 2.08-3.1c.47-1.3.7-2.74.7-4.34 0-1.6-.23-3.05-.7-4.34a6.88 6.88 0 0 0-2.08-3.12 5.27 5.27 0 0 0-3.47-1.16c-1.42 0-2.6.39-3.55 1.16a7 7 0 0 0-2.1 3.12c-.45 1.3-.68 2.74-.68 4.34 0 1.6.23 3.04.68 4.33a7 7 0 0 0 2.1 3.1 5.46 5.46 0 0 0 3.55 1.15Z"/>
|
||||
<path fill="#161616" d="M180.17 67.51c-2.56 0-4.77-.56-6.65-1.69a11.46 11.46 0 0 1-4.36-4.72 15.52 15.52 0 0 1-1.54-7.09c0-2.7.51-5.07 1.54-7.1a11.43 11.43 0 0 1 4.36-4.75 12.68 12.68 0 0 1 6.65-1.68c2.56 0 4.77.56 6.65 1.68a11.32 11.32 0 0 1 4.34 4.74 15.49 15.49 0 0 1 1.55 7.11c0 2.7-.51 5.07-1.55 7.1a11.34 11.34 0 0 1-4.34 4.71 12.68 12.68 0 0 1-6.65 1.7Zm.03-4.94c1.39 0 2.55-.38 3.48-1.14a6.89 6.89 0 0 0 2.08-3.1c.47-1.3.7-2.74.7-4.34 0-1.6-.23-3.05-.7-4.34a6.88 6.88 0 0 0-2.08-3.12 5.26 5.26 0 0 0-3.48-1.16c-1.42 0-2.6.39-3.54 1.16a7 7 0 0 0-2.1 3.12c-.45 1.3-.68 2.74-.68 4.34 0 1.6.23 3.04.68 4.33a7 7 0 0 0 2.1 3.1 5.46 5.46 0 0 0 3.54 1.15Z"/>
|
||||
<path fill="#161616" d="M162.13 32.1V67h-5.62l-16.45-23.78h-.3V67h-6.32V32.1h5.66l16.43 23.79h.31v-23.8h6.3Z"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill="#161616" d="M0 0h293.14v100H0z"/>
|
||||
</clipPath>
|
||||
<clipPath id="b">
|
||||
<path fill="#161616" d="M0 0h100v100H0z"/>
|
||||
</clipPath>
|
||||
<linearGradient id="c" x1="0" x2="100" y1="50" y2="50" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F77062"/>
|
||||
<stop offset="1" stop-color="#FE5196"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1,28 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="10 -10 304 120">
|
||||
<g clip-path="url(#a)">
|
||||
<g clip-path="url(#b)">
|
||||
<path fill="url(#c)" d="M72 0H28A28 28 0 0 0 0 28v44a28 28 0 0 0 28 28h44a28 28 0 0 0 28-28V28A28 28 0 0 0 72 0Z"/>
|
||||
<path stroke="#161616" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="m47.81 54.34 26.63-6.66-19.73-19.1-26.63 6.67 19.73 19.1Z"/>
|
||||
<path stroke="#161616" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="m47.81 54.34 18.23-4.56c.67 5.86-.29 11.8-2.76 17.15a31.87 31.87 0 0 0-19.65 3.01 31.87 31.87 0 0 0-15.5-12.43 32.2 32.2 0 0 1 6.18-16.23l13.5 13.06Z"/>
|
||||
<path stroke="#161616" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="m33.37 67.04 5.17-19.32 11.84-2.96m-2.57 9.58 26.63-6.66-19.73-19.1-26.63 6.67 19.73 19.1Zm0 0 18.23-4.56c.67 5.86-.29 11.8-2.76 17.15a31.87 31.87 0 0 0-19.65 3.01 31.87 31.87 0 0 0-15.5-12.43 32.2 32.2 0 0 1 6.18-16.23l13.5 13.06Z"/>
|
||||
</g>
|
||||
<path fill="#fff" d="M281.5 67.51c-2.63 0-4.9-.54-6.8-1.64a11.08 11.08 0 0 1-4.39-4.67 15.64 15.64 0 0 1-1.53-7.14c0-2.7.51-5.05 1.53-7.09a11.6 11.6 0 0 1 4.33-4.77 12.14 12.14 0 0 1 6.53-1.72c1.62 0 3.14.26 4.57.78 1.44.51 2.72 1.3 3.82 2.39a11.18 11.18 0 0 1 2.62 4.12c.64 1.66.96 3.64.96 5.93v1.9h-21.46v-4.16h15.54a6.63 6.63 0 0 0-.76-3.16 5.58 5.58 0 0 0-2.1-2.2 5.9 5.9 0 0 0-3.1-.8c-1.26 0-2.37.31-3.33.92-.95.6-1.7 1.4-2.23 2.4a6.88 6.88 0 0 0-.8 3.21v3.63c0 1.53.28 2.83.83 3.92a5.96 5.96 0 0 0 2.34 2.5c1 .56 2.17.84 3.51.84.9 0 1.71-.12 2.44-.37a5.4 5.4 0 0 0 1.89-1.14c.53-.5.94-1.12 1.21-1.86l5.76.65a9 9 0 0 1-2.08 3.99c-1 1.12-2.3 2-3.88 2.62-1.58.61-3.39.92-5.42.92Z"/>
|
||||
<path fill="#fff" d="M263.56 32.1V67h-6.17V32.1h6.17Z"/>
|
||||
<path fill="#fff" d="M236.87 67.46a9.9 9.9 0 0 1-5.52-1.58 10.77 10.77 0 0 1-3.85-4.6 17.18 17.18 0 0 1-1.42-7.34c0-2.9.48-5.36 1.43-7.36a10.6 10.6 0 0 1 3.9-4.55 10.03 10.03 0 0 1 5.48-1.55c1.53 0 2.8.26 3.78.78a7.13 7.13 0 0 1 2.35 1.86c.58.72 1.03 1.4 1.35 2.03h.26V32.09h6.18V67h-6.06v-4.13h-.38c-.32.64-.78 1.32-1.38 2.03a7.6 7.6 0 0 1-6.12 2.56Zm1.72-5.06c1.3 0 2.42-.35 3.34-1.06a6.6 6.6 0 0 0 2.1-2.98c.48-1.27.72-2.76.72-4.45 0-1.7-.24-3.17-.72-4.42a6.28 6.28 0 0 0-2.08-2.91 5.38 5.38 0 0 0-3.36-1.04c-1.37 0-2.52.36-3.44 1.07a6.5 6.5 0 0 0-2.08 2.97c-.47 1.26-.7 2.7-.7 4.33 0 1.64.23 3.1.7 4.38a6.84 6.84 0 0 0 2.1 3.02 5.4 5.4 0 0 0 3.42 1.09Z"/>
|
||||
<path fill="#fff" d="M209.37 67.51c-2.56 0-4.77-.56-6.65-1.69a11.46 11.46 0 0 1-4.36-4.72 15.52 15.52 0 0 1-1.53-7.09c0-2.7.5-5.07 1.53-7.1a11.43 11.43 0 0 1 4.36-4.75 12.68 12.68 0 0 1 6.65-1.68c2.56 0 4.78.56 6.65 1.68a11.32 11.32 0 0 1 4.35 4.74 15.47 15.47 0 0 1 1.55 7.11c0 2.7-.52 5.07-1.55 7.1a11.34 11.34 0 0 1-4.35 4.71 12.68 12.68 0 0 1-6.65 1.7Zm.04-4.94c1.38 0 2.54-.38 3.47-1.14a6.89 6.89 0 0 0 2.08-3.1c.47-1.3.7-2.74.7-4.34 0-1.6-.23-3.05-.7-4.34a6.88 6.88 0 0 0-2.08-3.12 5.27 5.27 0 0 0-3.47-1.16c-1.42 0-2.6.39-3.55 1.16a7 7 0 0 0-2.1 3.12c-.45 1.3-.68 2.74-.68 4.34 0 1.6.23 3.04.68 4.33a7 7 0 0 0 2.1 3.1 5.46 5.46 0 0 0 3.55 1.15Z"/>
|
||||
<path fill="#fff" d="M180.17 67.51c-2.56 0-4.77-.56-6.65-1.69a11.46 11.46 0 0 1-4.36-4.72 15.52 15.52 0 0 1-1.54-7.09c0-2.7.51-5.07 1.54-7.1a11.43 11.43 0 0 1 4.36-4.75 12.68 12.68 0 0 1 6.65-1.68c2.56 0 4.77.56 6.65 1.68a11.32 11.32 0 0 1 4.34 4.74 15.49 15.49 0 0 1 1.55 7.11c0 2.7-.51 5.07-1.55 7.1a11.34 11.34 0 0 1-4.34 4.71 12.68 12.68 0 0 1-6.65 1.7Zm.03-4.94c1.39 0 2.55-.38 3.48-1.14a6.89 6.89 0 0 0 2.08-3.1c.47-1.3.7-2.74.7-4.34 0-1.6-.23-3.05-.7-4.34a6.88 6.88 0 0 0-2.08-3.12 5.26 5.26 0 0 0-3.48-1.16c-1.42 0-2.6.39-3.54 1.16a7 7 0 0 0-2.1 3.12c-.45 1.3-.68 2.74-.68 4.34 0 1.6.23 3.04.68 4.33a7 7 0 0 0 2.1 3.1 5.46 5.46 0 0 0 3.54 1.15Z"/>
|
||||
<path fill="#fff" d="M162.13 32.1V67h-5.62l-16.45-23.78h-.3V67h-6.32V32.1h5.66l16.43 23.79h.31v-23.8h6.3Z"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill="#fff" d="M0 0h293.14v100H0z"/>
|
||||
</clipPath>
|
||||
<clipPath id="b">
|
||||
<path fill="#fff" d="M0 0h100v100H0z"/>
|
||||
</clipPath>
|
||||
<linearGradient id="c" x1="0" x2="100" y1="50" y2="50" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F77062"/>
|
||||
<stop offset="1" stop-color="#FE5196"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.1 KiB |
@@ -1,9 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 151 31">
|
||||
<path fill="#111" d="M49.37 7.6c-8.39 0-10.49 1.8-11.13 5.39-.62 3.58.93 5.06 8.1 5.06 4.2 0 5.39 0 5.2 1.04-.2 1.06-1.4 1.06-4.4 1.06-3 0-4.19 0-3.96-1.2H36.9c-.74 4.2 1.13 5.4 9.53 5.4 8.39 0 10.68-1.2 11.42-5.4.65-3.58-1.2-5.1-8.39-5.1-4.2 0-5.39-.02-5.2-1.03.2-1.06 1.4-1.06 4.4-1.06 2.38 0 4.19 0 3.96 1.2h5.97c.65-3.69-.83-5.36-9.23-5.36Z"/>
|
||||
<path fill="#FFC158" d="M.84 15.81C.84 4.45 4.63.66 16 .66c11.37 0 15.15 3.79 15.15 15.15S27.36 30.96 16 30.96C4.63 30.96.84 27.17.84 15.81Z"/>
|
||||
<path fill="#fff" fill-rule="evenodd" d="m16.16 8.37-5.8 8.87c-.18.28.02.65.36.65h5.32l-.99 5.37c-.08.46.52.7.77.31l5.8-8.86a.42.42 0 0 0-.36-.66H15.94l1-5.37c.08-.46-.52-.7-.78-.31Z" clip-rule="evenodd"/>
|
||||
<path fill="#111" d="m77.04 8.18-1.38 7.78c-.53 3-2.9 4.2-5.33 4.2s-4.37-1.2-3.82-4.2l1.39-7.78h-6.71l-1.42 7.78c-1.08 5.97 2.14 8.39 7.63 8.39 4.28 0 7.04-1.81 7.04-1.81l.4 1.2h6.08l2.8-15.56h-6.68Z"/>
|
||||
<path fill="#111" d="M98.83 7.6c-4.27 0-6.41 1.8-6.41 1.8l.23-1.19h-6.7l-4.09 22.75h6.71l1.51-8.39s1.52 1.8 5.8 1.8c5.48 0 10.05-1.8 11.24-8.38 1.15-6.62-2.8-8.4-8.29-8.4Zm1.55 8.36c-.53 3-2.9 4.2-5.33 4.2s-4.37-1.2-3.82-4.2c.53-3 2.9-4.2 5.33-4.2s4.34 1.2 3.82 4.2Z"/>
|
||||
<path fill="#111" d="M121.08 7.6c-7.3 0-11.67 2.26-12.76 8.23-1.08 5.97 2.47 8.52 9.77 8.52 5.13 0 7.92-.9 10.55-3.23l-3.91-2.49c-.86.52-1.78 1.52-5.89 1.52-2.76 0-4-1.03-3.94-3.13h15.72c.1-.39.2-.77.26-1.2 1.05-6-2.5-8.22-9.8-8.22Zm-5.52 6.42c.78-1.68 2-2.26 4.76-2.26 2.73 0 3.78.55 3.95 2.26h-8.71Z"/>
|
||||
<path fill="#111" d="m140.33 9.37.23-1.2h-6.7l-2.8 15.56h6.7l1.19-6.58c.76-4.2 3.91-4.78 6.35-4.78 2.4 0 3.19.26 4.76.62l.86-4.78c-.86-.26-2.9-.61-4.77-.61-4.27 0-5.82 1.77-5.82 1.77Z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,9 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 151 31">
|
||||
<path fill="#fff" d="M49.37 7.6c-8.39 0-10.49 1.8-11.13 5.39-.62 3.58.93 5.06 8.1 5.06 4.2 0 5.39 0 5.2 1.04-.2 1.06-1.4 1.06-4.4 1.06-3 0-4.19 0-3.96-1.2H36.9c-.74 4.2 1.13 5.4 9.53 5.4 8.39 0 10.68-1.2 11.42-5.4.65-3.58-1.2-5.1-8.39-5.1-4.2 0-5.39-.02-5.2-1.03.2-1.06 1.4-1.06 4.4-1.06 2.38 0 4.19 0 3.96 1.2h5.97c.65-3.69-.83-5.36-9.23-5.36Z"/>
|
||||
<path fill="#FFC158" d="M.84 15.81C.84 4.45 4.63.66 16 .66c11.37 0 15.15 3.79 15.15 15.15S27.36 30.96 16 30.96C4.63 30.96.84 27.17.84 15.81Z"/>
|
||||
<path fill="#fff" fill-rule="evenodd" d="m16.16 8.37-5.8 8.87c-.18.28.02.65.36.65h5.32l-.99 5.37c-.08.46.52.7.77.31l5.8-8.86a.42.42 0 0 0-.36-.66H15.94l1-5.37c.08-.46-.52-.7-.78-.31Z" clip-rule="evenodd"/>
|
||||
<path fill="#fff" d="m77.04 8.18-1.38 7.78c-.53 3-2.9 4.2-5.33 4.2s-4.37-1.2-3.82-4.2l1.39-7.78h-6.71l-1.42 7.78c-1.08 5.97 2.14 8.39 7.63 8.39 4.28 0 7.04-1.81 7.04-1.81l.4 1.2h6.08l2.8-15.56h-6.68Z"/>
|
||||
<path fill="#fff" d="M98.83 7.6c-4.27 0-6.41 1.8-6.41 1.8l.23-1.19h-6.7l-4.09 22.75h6.71l1.51-8.39s1.52 1.8 5.8 1.8c5.48 0 10.05-1.8 11.24-8.38 1.15-6.62-2.8-8.4-8.29-8.4Zm1.55 8.36c-.53 3-2.9 4.2-5.33 4.2s-4.37-1.2-3.82-4.2c.53-3 2.9-4.2 5.33-4.2s4.34 1.2 3.82 4.2Z"/>
|
||||
<path fill="#fff" d="M121.08 7.6c-7.3 0-11.67 2.26-12.76 8.23-1.08 5.97 2.47 8.52 9.77 8.52 5.13 0 7.92-.9 10.55-3.23l-3.91-2.49c-.86.52-1.78 1.52-5.89 1.52-2.76 0-4-1.03-3.94-3.13h15.72c.1-.39.2-.77.26-1.2 1.05-6-2.5-8.22-9.8-8.22Zm-5.52 6.42c.78-1.68 2-2.26 4.76-2.26 2.73 0 3.78.55 3.95 2.26h-8.71Z"/>
|
||||
<path fill="#fff" d="m140.33 9.37.23-1.2h-6.7l-2.8 15.56h6.7l1.19-6.58c.76-4.2 3.91-4.78 6.35-4.78 2.4 0 3.19.26 4.76.62l.86-4.78c-.86-.26-2.9-.61-4.77-.61-4.27 0-5.82 1.77-5.82 1.77Z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
6
docs/public/framework-logos/lit.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 200" fill="none">
|
||||
<path fill="#00e8ff" d="M40 120l20-60l90 90l-30 50l-40-40h-20" />
|
||||
<path fill="#283198" d="M80 160 L80 80 L120 40 L 120 120 M0 160 L40 200 L40 120 L20 120" />
|
||||
<path fill="#324fff" d="M40 120v-80l40-40v80M120 200v-80l40-40v80M0 160v-80l40 40" />
|
||||
<path fill="#0ff" d="M40 200v-80l40 40" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 377 B |
@@ -23,12 +23,12 @@ const writeIconFiles = icons.map(async (iconFileName) => {
|
||||
const location = path.resolve(iconDetailsDirectory, `${iconName}.ts`);
|
||||
|
||||
const contents = `\
|
||||
import iconNode from '../iconNodes/${iconName}.node.json'
|
||||
import metaData from '../../../../icons/${iconName}.json'
|
||||
import releaseData from '../releaseMetadata/${iconName}.json'
|
||||
import popularity from '../iconPopularity/${iconName}.json'
|
||||
import iconNode from '../iconNodes/${iconName}.node.json' with { type: 'json' };
|
||||
import metaData from '../../../../icons/${iconName}.json' with { type: 'json' };
|
||||
import releaseData from '../releaseMetadata/${iconName}.json' with { type: 'json' };
|
||||
import popularity from '../iconPopularity/${iconName}.json' with { type: 'json' };
|
||||
|
||||
const { tags, categories, contributors, aliases, deprecated, deprecationReason, toBeRemovedInVersion } = metaData
|
||||
const { tags, categories, contributors, aliases, deprecated, deprecationReason, toBeRemovedInVersion } = metaData;
|
||||
|
||||
const iconDetails = {
|
||||
name: '${iconName}',
|
||||
@@ -42,9 +42,9 @@ const iconDetails = {
|
||||
deprecationReason,
|
||||
toBeRemovedInVersion,
|
||||
...releaseData,
|
||||
}
|
||||
};
|
||||
|
||||
export default iconDetails
|
||||
export default iconDetails;
|
||||
`;
|
||||
|
||||
await fs.promises.writeFile(location, contents, 'utf-8');
|
||||
|
||||
49
docs/scripts/writeLabIconMetaIndex.mts
Normal file
@@ -0,0 +1,49 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { readSvgDirectory, toCamelCase } from '@lucide/helpers';
|
||||
|
||||
const currentDir = process.cwd();
|
||||
const ICONS_DIR = path.resolve(currentDir, '../lab');
|
||||
const iconJsonFiles = await readSvgDirectory(ICONS_DIR, '.json');
|
||||
|
||||
const labDirectory = path.resolve(currentDir, '.vitepress/data/lab');
|
||||
const location = path.resolve(currentDir, labDirectory, 'iconMetaData.ts');
|
||||
|
||||
if (fs.existsSync(location)) {
|
||||
fs.unlinkSync(location);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(labDirectory)) {
|
||||
fs.mkdirSync(labDirectory);
|
||||
}
|
||||
|
||||
const iconMetaIndexFileImports = [];
|
||||
const iconMetaIndexFileExports = [];
|
||||
|
||||
iconJsonFiles.forEach((iconJsonFile) => {
|
||||
const iconName = path.basename(iconJsonFile, '.json');
|
||||
|
||||
iconMetaIndexFileImports.push(
|
||||
`import ${toCamelCase(iconName)}Metadata from '../../../../lab/${iconName}.json';`,
|
||||
);
|
||||
iconMetaIndexFileExports.push(` '${iconName}': ${toCamelCase(iconName)}Metadata,`);
|
||||
});
|
||||
|
||||
try {
|
||||
await fs.promises.writeFile(
|
||||
location,
|
||||
`\
|
||||
${iconMetaIndexFileImports.join('\n')}
|
||||
|
||||
|
||||
export default {
|
||||
${iconMetaIndexFileExports.join('\n')}
|
||||
}
|
||||
`,
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
console.log('Successfully write icon json file index');
|
||||
} catch (error) {
|
||||
throw new Error(`Something went wrong generating icon json file index file,\n ${error}`);
|
||||
}
|
||||
62
docs/scripts/writeLabIconNodes.mts
Normal file
@@ -0,0 +1,62 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { renderIconsObject } from '@lucide/build-icons';
|
||||
import { readSvgDirectory, toCamelCase } from '@lucide/helpers';
|
||||
|
||||
const currentDir = process.cwd();
|
||||
const ICONS_DIR = path.resolve(currentDir, '../lab');
|
||||
const svgFiles = await readSvgDirectory(ICONS_DIR);
|
||||
const icons = await renderIconsObject(svgFiles, ICONS_DIR, true);
|
||||
|
||||
const iconNodesDirectory = path.resolve(currentDir, '.vitepress/data/lab', 'iconNodes');
|
||||
|
||||
if (fs.existsSync(iconNodesDirectory)) {
|
||||
fs.rmSync(iconNodesDirectory, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
if (!fs.existsSync(iconNodesDirectory)) {
|
||||
fs.mkdirSync(iconNodesDirectory);
|
||||
}
|
||||
|
||||
const iconIndexFile = path.resolve(iconNodesDirectory, `index.ts`);
|
||||
const iconIndexFileImports: string[] = [];
|
||||
const iconIndexFileExports: string[] = [];
|
||||
const iconIndexFileDefaultExports: string[] = [];
|
||||
|
||||
const writeIconFiles = Object.entries(icons).map(async ([iconName, { children }]) => {
|
||||
// We need to use .node.json because the there is a file called package, which is a reserved word for packages.
|
||||
const location = path.resolve(iconNodesDirectory, `${iconName}.node.json`);
|
||||
const iconNode = children.map(({ name, attributes }) => [name, attributes]);
|
||||
|
||||
const output = JSON.stringify(iconNode, null, 2);
|
||||
await fs.promises.writeFile(location, output, 'utf-8');
|
||||
|
||||
iconIndexFileImports.push(
|
||||
`import ${toCamelCase(iconName)}Node from './${iconName}.node.json' with { type: "json" };`,
|
||||
);
|
||||
iconIndexFileExports.push(` ${toCamelCase(iconName)}Node as ${toCamelCase(iconName)},`);
|
||||
iconIndexFileDefaultExports.push(` '${iconName}': ${toCamelCase(iconName)}Node,`);
|
||||
});
|
||||
|
||||
try {
|
||||
await Promise.all(writeIconFiles);
|
||||
await fs.promises.writeFile(
|
||||
iconIndexFile,
|
||||
`\
|
||||
${iconIndexFileImports.join('\n')}
|
||||
|
||||
export {
|
||||
${iconIndexFileExports.join('\n')}
|
||||
}
|
||||
|
||||
export default {
|
||||
${iconIndexFileDefaultExports.join('\n')}
|
||||
}
|
||||
`,
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
console.log('Successfully write', writeIconFiles.length, 'iconNodes.');
|
||||
} catch (error) {
|
||||
throw new Error(`Something went wrong generating iconNode files,\n ${error}`);
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-restricted-syntax, no-await-in-loop */
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { simpleGit } from 'simple-git';
|
||||
|
||||
@@ -1,31 +1,64 @@
|
||||
/* eslint-disable no-restricted-syntax, no-await-in-loop */
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { loadEnvFile } from 'node:process';
|
||||
import { readSvgDirectory } from '@lucide/helpers';
|
||||
|
||||
const currentDir = process.cwd();
|
||||
const ICONS_DIR = path.resolve(currentDir, '../icons');
|
||||
const iconPopularityDirectory = path.resolve(currentDir, '.vitepress/data', 'iconPopularity');
|
||||
const dataDirectory = path.resolve(currentDir, '.vitepress/data');
|
||||
|
||||
try {
|
||||
// Load environment variables from .env file, if it exists.
|
||||
loadEnvFile(`${currentDir}/.env`);
|
||||
} catch (error) {}
|
||||
|
||||
if (fs.existsSync(iconPopularityDirectory)) {
|
||||
fs.rmSync(iconPopularityDirectory, { recursive: true, force: true });
|
||||
} catch {
|
||||
// No .env file, so rely on the environment as-is.
|
||||
}
|
||||
|
||||
if (!fs.existsSync(iconPopularityDirectory)) {
|
||||
fs.mkdirSync(iconPopularityDirectory);
|
||||
interface IconCollection {
|
||||
/** Human readable name, used for logging. */
|
||||
name: string;
|
||||
/** Directory containing the icon `.json` files. */
|
||||
iconsDir: string;
|
||||
/** Directory the `${iconName}.json` popularity files are written to. */
|
||||
outputDir: string;
|
||||
/** Matches an analytics route and captures the icon name in group 1. */
|
||||
routePattern: RegExp;
|
||||
}
|
||||
|
||||
const iconJsonFiles = await readSvgDirectory(ICONS_DIR, '.json');
|
||||
const iconNames = iconJsonFiles.map((file) => path.basename(file, '.json'));
|
||||
// Lab must come before the default collection because `/icons/lab/foo` also
|
||||
// matches the default `/icons/(...)` pattern; the first match wins per route.
|
||||
const iconCollections: IconCollection[] = [
|
||||
{
|
||||
name: 'lab',
|
||||
iconsDir: path.resolve(currentDir, '../lab'),
|
||||
outputDir: path.resolve(dataDirectory, 'lab', 'iconPopularity'),
|
||||
// e.g. /icons/lab/burger, /icons/lab/burger/ or /icons/lab/burger?ref=homepage
|
||||
routePattern: /^\/icons\/lab\/([^\/\?]+)(?:[\/\?]|$)/,
|
||||
},
|
||||
{
|
||||
name: 'default',
|
||||
iconsDir: path.resolve(currentDir, '../icons'),
|
||||
outputDir: path.resolve(dataDirectory, 'iconPopularity'),
|
||||
// e.g. /icons/activity, /icons/activity/ or /icons/activity?ref=homepage
|
||||
routePattern: /^\/icons\/([^\/\?]+)(?:[\/\?]|$)/,
|
||||
},
|
||||
];
|
||||
|
||||
const defaultIconNamePopularity: Record<string, number> = Object.fromEntries(
|
||||
iconNames.map((iconName) => [iconName, 0])
|
||||
interface CollectionData extends IconCollection {
|
||||
iconNames: string[];
|
||||
counts: Record<string, number>;
|
||||
}
|
||||
|
||||
const collections: CollectionData[] = await Promise.all(
|
||||
iconCollections.map(async (collection) => {
|
||||
const iconJsonFiles = await readSvgDirectory(collection.iconsDir, '.json');
|
||||
const iconNames = iconJsonFiles.map((file) => path.basename(file, '.json'));
|
||||
|
||||
return {
|
||||
...collection,
|
||||
iconNames,
|
||||
counts: Object.fromEntries(iconNames.map((iconName) => [iconName, 0])),
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
interface AnalyticsResponse {
|
||||
@@ -35,19 +68,30 @@ interface AnalyticsResponse {
|
||||
}[];
|
||||
}
|
||||
|
||||
const writeIconPopularity = async (iconVisitCounts: Record<string, number>) => {
|
||||
const writePopularityPromises = Object.entries(iconVisitCounts).map(([iconName, count]) => {
|
||||
const location = path.resolve(iconPopularityDirectory, `${iconName}.json`);
|
||||
const writeIconPopularity = async ({ name, outputDir, counts }: CollectionData) => {
|
||||
if (fs.existsSync(outputDir)) {
|
||||
fs.rmSync(outputDir, { recursive: true, force: true });
|
||||
}
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
|
||||
const writePopularityPromises = Object.entries(counts).map(([iconName, count]) => {
|
||||
const location = path.resolve(outputDir, `${iconName}.json`);
|
||||
return fs.promises.writeFile(location, JSON.stringify({ count }, null, 2), 'utf-8');
|
||||
});
|
||||
|
||||
await Promise.all(writePopularityPromises);
|
||||
console.log('Successfully written icon popularity data for', writePopularityPromises.length, 'icons.');
|
||||
console.log(
|
||||
'Successfully written icon popularity data for',
|
||||
writePopularityPromises.length,
|
||||
`${name} icons.`,
|
||||
);
|
||||
};
|
||||
|
||||
const writeAllIconPopularity = () => Promise.all(collections.map(writeIconPopularity));
|
||||
|
||||
if (process.env.LUCIDE_ANALYTICS_TOKEN === undefined) {
|
||||
console.warn('LUCIDE_ANALYTICS_TOKEN environment variable is not set. Writing default icon popularity data.');
|
||||
await writeIconPopularity(defaultIconNamePopularity);
|
||||
await writeAllIconPopularity();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
@@ -76,29 +120,34 @@ try {
|
||||
|
||||
const analyticsData: AnalyticsResponse = await response.json();
|
||||
|
||||
const iconVisitCounts = analyticsData.results.reduce((acc, { dimensions, metrics }) => {
|
||||
for (const { dimensions, metrics } of analyticsData.results) {
|
||||
const [route] = dimensions;
|
||||
const [count] = metrics;
|
||||
|
||||
if (!route.startsWith('/icons')) {
|
||||
return acc
|
||||
continue;
|
||||
}
|
||||
// Only match routes that start with /icons/ and end with an icon name, e.g. /icons/activity, /icons/activity/ or /icons/activity?ref=homepage
|
||||
const match = route.match(/^\/icons\/([^\/\?]+)(?:[\/\?]|$)/);
|
||||
if (match) {
|
||||
const iconName = match[1];
|
||||
|
||||
if (iconNames.includes(iconName)) {
|
||||
acc[iconName] = (acc[iconName] || 0) + count;
|
||||
// Route each visit to the first collection whose pattern matches. Lab is
|
||||
// checked first so `/icons/lab/foo` is not swallowed by the default pattern.
|
||||
for (const collection of collections) {
|
||||
const match = route.match(collection.routePattern);
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const iconName = match[1];
|
||||
if (collection.iconNames.includes(iconName)) {
|
||||
collection.counts[iconName] += count;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, defaultIconNamePopularity);
|
||||
|
||||
await writeIconPopularity(iconVisitCounts);
|
||||
await writeAllIconPopularity();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching analytics data:', error);
|
||||
await writeIconPopularity(defaultIconNamePopularity);
|
||||
await writeAllIconPopularity();
|
||||
}
|
||||
|
||||
49
docs/scripts/writeiconPopularityIndex.mts
Normal file
@@ -0,0 +1,49 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { readSvgDirectory, toCamelCase } from '@lucide/helpers';
|
||||
|
||||
const currentDir = process.cwd();
|
||||
const ICONS_DIR = path.resolve(currentDir, '../lab');
|
||||
const iconJsonFiles = await readSvgDirectory(ICONS_DIR, '.json');
|
||||
|
||||
const labIconPopularityDirectory = path.resolve(currentDir, '.vitepress/data/lab/iconPopularity');
|
||||
const location = path.resolve(currentDir, labIconPopularityDirectory, 'index.ts');
|
||||
|
||||
if (fs.existsSync(location)) {
|
||||
fs.unlinkSync(location);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(labIconPopularityDirectory)) {
|
||||
fs.mkdirSync(labIconPopularityDirectory);
|
||||
}
|
||||
|
||||
const iconMetaIndexFileImports = [];
|
||||
const iconMetaIndexFileExports = [];
|
||||
|
||||
iconJsonFiles.forEach((iconJsonFile) => {
|
||||
const iconName = path.basename(iconJsonFile, '.json');
|
||||
|
||||
iconMetaIndexFileImports.push(
|
||||
`import ${toCamelCase(iconName)}Popularity from './${iconName}.json' with { type: 'json' };`,
|
||||
);
|
||||
iconMetaIndexFileExports.push(` '${iconName}': ${toCamelCase(iconName)}Popularity,`);
|
||||
});
|
||||
|
||||
try {
|
||||
await fs.promises.writeFile(
|
||||
location,
|
||||
`\
|
||||
${iconMetaIndexFileImports.join('\n')}
|
||||
|
||||
|
||||
export default {
|
||||
${iconMetaIndexFileExports.join('\n')}
|
||||
}
|
||||
`,
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
console.log('Successfully write icon json file index');
|
||||
} catch (error) {
|
||||
throw new Error(`Something went wrong generating icon json file index file,\n ${error}`);
|
||||
}
|
||||
154
eslint.config.js
Normal file
@@ -0,0 +1,154 @@
|
||||
import path from 'node:path';
|
||||
import js from '@eslint/js';
|
||||
import { includeIgnoreFile } from '@eslint/compat';
|
||||
import { defineConfig } from 'eslint/config';
|
||||
import prettier from 'eslint-config-prettier';
|
||||
import importX, { createNodeResolver } from 'eslint-plugin-import-x';
|
||||
import htmlEslint from '@html-eslint/eslint-plugin';
|
||||
import htmlParser from '@html-eslint/parser';
|
||||
import defaultAttrs from './tools/build-icons/render/default-attrs.json' with { type: 'json' };
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
const gitignorePath = path.join(import.meta.dirname, '.gitignore');
|
||||
|
||||
export default defineConfig([
|
||||
tseslint.configs.recommended,
|
||||
{
|
||||
// `packages/angular` has its own config with its own tsconfig, so a single `eslint .` run sees
|
||||
// two candidate roots and typescript-eslint refuses to guess between them.
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Everything git ignores (generated icon sources, build output, caches) is ignored here too.
|
||||
includeIgnoreFile(gitignorePath),
|
||||
{
|
||||
// Ignores that are not in .gitignore, because these files are committed.
|
||||
ignores: [
|
||||
'lib',
|
||||
'**/tests',
|
||||
'packages/**/tests/*',
|
||||
'docs/images',
|
||||
'docs/**/examples/',
|
||||
'docs/.vitepress/theme/components/editors/preact/index.js',
|
||||
'packages/svelte/.svelte-kit',
|
||||
// Tracked in git despite matching a .gitignore pattern, so lint it.
|
||||
'!packages/lucide-react/dynamicIconImports.mjs',
|
||||
],
|
||||
},
|
||||
{
|
||||
files: ['**/*.js'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
console: 'readonly',
|
||||
process: 'readonly',
|
||||
global: 'readonly',
|
||||
__dirname: 'readonly',
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
'import-x': importX,
|
||||
},
|
||||
settings: {
|
||||
'import-x/resolver-next': [createNodeResolver()],
|
||||
},
|
||||
rules: {
|
||||
...js.configs.recommended.rules,
|
||||
...prettier.rules,
|
||||
'no-console': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'no-shadow': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'import-x/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
devDependencies: [
|
||||
'**/*.test.js',
|
||||
'**/*.spec.js',
|
||||
'**/scripts/**',
|
||||
'eslint.config.js',
|
||||
'packages/**/tests/**',
|
||||
],
|
||||
},
|
||||
],
|
||||
'import-x/extensions': [
|
||||
'error',
|
||||
{
|
||||
pattern: {
|
||||
mjs: 'always',
|
||||
json: 'always',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
// Omitting a property by destructuring it away (`const { key, ...attrs } = node`) leaves the
|
||||
// omitted binding unused on purpose, so don't report it.
|
||||
'@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true }],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['./icons/*.svg'],
|
||||
languageOptions: {
|
||||
parser: htmlParser,
|
||||
},
|
||||
plugins: {
|
||||
'@html-eslint': htmlEslint,
|
||||
},
|
||||
rules: {
|
||||
'@html-eslint/require-doctype': 'off',
|
||||
'@html-eslint/no-duplicate-attrs': 'error',
|
||||
'@html-eslint/no-inline-styles': 'error',
|
||||
'@html-eslint/require-attrs': [
|
||||
'error',
|
||||
...Object.entries(defaultAttrs).map(([attr, value]) => ({
|
||||
tag: 'svg',
|
||||
attr,
|
||||
value: String(value),
|
||||
})),
|
||||
],
|
||||
'@html-eslint/indent': ['error', 2],
|
||||
'@html-eslint/no-multiple-empty-lines': ['error', { max: 0 }],
|
||||
'@html-eslint/no-extra-spacing-attrs': [
|
||||
'error',
|
||||
{
|
||||
enforceBeforeSelfClose: true,
|
||||
},
|
||||
],
|
||||
'@html-eslint/attrs-newline': [
|
||||
'error',
|
||||
{
|
||||
inline: ['path', 'line', 'polyline', 'polygon', 'rect', 'circle', 'ellipse'],
|
||||
},
|
||||
],
|
||||
'@html-eslint/require-closing-tags': [
|
||||
'error',
|
||||
{
|
||||
selfClosing: 'always',
|
||||
// Inside <svg> every tag counts as "foreign", where the rule only checks that a tag
|
||||
// already written `/>` stays that way. Listing the shapes here is what actually forces
|
||||
// `<path ...></path>` to become `<path ... />`.
|
||||
selfClosingCustomPatterns: ['^(path|line|polyline|polygon|rect|circle|ellipse)$'],
|
||||
},
|
||||
],
|
||||
'@html-eslint/no-restricted-attr-values': [
|
||||
'error',
|
||||
{
|
||||
attrPatterns: ['^(fill|stroke)$'],
|
||||
attrValuePatterns: ['^(?!(none|currentColor)$).*$'],
|
||||
message:
|
||||
'Icons must inherit their colors: `fill` and `stroke` may only be `none` or `currentColor`.',
|
||||
},
|
||||
],
|
||||
'@html-eslint/element-newline': 'error',
|
||||
'@html-eslint/no-trailing-spaces': 'error',
|
||||
'@html-eslint/quotes': 'error',
|
||||
},
|
||||
},
|
||||
]);
|
||||
@@ -40,7 +40,7 @@
|
||||
}
|
||||
},
|
||||
"dependentRequired": {
|
||||
"deprecated": ["deprecationReason", "toBeRemovedInVersion"],
|
||||
"deprecated": ["deprecationReason"],
|
||||
"deprecationReason": ["deprecated"],
|
||||
"toBeRemovedInVersion": ["deprecated"]
|
||||
}
|
||||
@@ -134,7 +134,7 @@
|
||||
}
|
||||
},
|
||||
"dependentRequired": {
|
||||
"deprecated": ["deprecationReason", "toBeRemovedInVersion"],
|
||||
"deprecated": ["deprecationReason"],
|
||||
"deprecationReason": ["deprecated"],
|
||||
"toBeRemovedInVersion": ["deprecated"]
|
||||
},
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
<path d="M15.697 14h5.606" />
|
||||
<path d="m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16" />
|
||||
<path d="M3.304 13h6.392" />
|
||||
</svg>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 390 B After Width: | Height: | Size: 391 B |
@@ -21,10 +21,15 @@
|
||||
"paid",
|
||||
"partner",
|
||||
"promo",
|
||||
"sponsor"
|
||||
"sponsor",
|
||||
"audio description",
|
||||
"video description",
|
||||
"described video",
|
||||
"visual description"
|
||||
],
|
||||
"categories": [
|
||||
"multimedia",
|
||||
"accessibility",
|
||||
"notifications"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
{
|
||||
"name": "alarm-check",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
{
|
||||
"name": "alarm-minus",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
{
|
||||
"name": "alarm-plus",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
"deprecated": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11,10 +11,9 @@
|
||||
>
|
||||
<path d="M10 10H6" />
|
||||
<path d="M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2" />
|
||||
<path
|
||||
d="M19 18h2a1 1 0 0 0 1-1v-3.28a1 1 0 0 0-.684-.948l-1.923-.641a1 1 0 0 1-.578-.502l-1.539-3.076A1 1 0 0 0 16.382 8H14" />
|
||||
<path d="M19 18h2a1 1 0 0 0 1-1v-3.28a1 1 0 0 0-.684-.948l-1.923-.641a1 1 0 0 1-.578-.502l-1.539-3.076A1 1 0 0 0 16.382 8H14" />
|
||||
<path d="M8 8v4" />
|
||||
<path d="M9 18h6" />
|
||||
<circle cx="17" cy="18" r="2" />
|
||||
<circle cx="7" cy="18" r="2" />
|
||||
</svg>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 553 B After Width: | Height: | Size: 550 B |
34
icons/angle.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"$schema": "../icon.schema.json",
|
||||
"contributors": [
|
||||
"samuelalake"
|
||||
],
|
||||
"use-cases": [
|
||||
"rotation input in a design inspector",
|
||||
"measurement tool in cad software",
|
||||
"dimensioning in technical drawings",
|
||||
"labeling angles in geometry exercises",
|
||||
"displaying degrees for rotation controls"
|
||||
],
|
||||
"tags": [
|
||||
"angle",
|
||||
"degree",
|
||||
"corner",
|
||||
"geometry",
|
||||
"measure",
|
||||
"math",
|
||||
"rotate",
|
||||
"vertex",
|
||||
"slope",
|
||||
"protractor",
|
||||
"measurement",
|
||||
"dimension",
|
||||
"arc",
|
||||
"drafting",
|
||||
"cad"
|
||||
],
|
||||
"categories": [
|
||||
"math",
|
||||
"design"
|
||||
]
|
||||
}
|
||||
14
icons/angle.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M3 3v16a2 2 0 0 0 2 2h16" />
|
||||
<path d="M3 11a10 10 0 0 1 10 10" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 287 B |
@@ -25,8 +25,8 @@
|
||||
{
|
||||
"name": "arrow-down-01",
|
||||
"deprecationReason": "alias.name",
|
||||
"deprecated": true,
|
||||
"toBeRemovedInVersion": "v1.0"
|
||||
}
|
||||
"deprecated": true
|
||||
},
|
||||
"arrow-down-01"
|
||||
]
|
||||
}
|
||||
|
||||