diff --git a/.cspell/custom-words.txt b/.cspell/custom-words.txt index e0e6b2098..39dc31c30 100644 --- a/.cspell/custom-words.txt +++ b/.cspell/custom-words.txt @@ -9,9 +9,3 @@ strikethrough touchpad ungroup toc - -# Brands -codepen -codesandbox -dribbble -x.com diff --git a/.eslintignore b/.eslintignore index ffae92223..afce5388f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -8,3 +8,4 @@ node_modules docs/images docs/**/examples/ packages/lucide-react/dynamicIconImports.js +packages/angular/.angular diff --git a/.github/ISSUE_TEMPLATE/02_bug_report.yml b/.github/ISSUE_TEMPLATE/02_bug_report.yml index d8567aca2..f5d8918bb 100644 --- a/.github/ISSUE_TEMPLATE/02_bug_report.yml +++ b/.github/ISSUE_TEMPLATE/02_bug_report.yml @@ -13,16 +13,19 @@ body: description: Which Lucide packages are affected? You may select more than one. options: - label: lucide - - label: lucide-angular - - label: lucide-flutter - - label: lucide-preact - label: lucide-react + - label: '@lucide/vue' + - label: '@lucide/svelte' + - label: '@lucide/angular' + - label: '@lucide/astro' - label: lucide-react-native + - label: lucide-preact - label: lucide-solid - - label: lucide-svelte - - label: lucide-vue - - label: lucide-vue-next - - label: lucide-astro + - label: lucide-static + - label: '@lucide/icons' + - label: lucide-vue-next (deprecated) + - label: lucide-svelte (deprecated) + - label: lucide-angular (deprecated) - label: Figma plugin - label: source/main - label: other/not relevant @@ -33,7 +36,7 @@ body: attributes: label: Version description: What version of Lucide are you running? - placeholder: e.g. 0.289.1 + placeholder: e.g. 1.1.0 validations: required: true - type: checkboxes diff --git a/.github/ISSUE_TEMPLATE/04_feature_request.yml b/.github/ISSUE_TEMPLATE/04_feature_request.yml index 63a2469ec..73d354ac8 100644 --- a/.github/ISSUE_TEMPLATE/04_feature_request.yml +++ b/.github/ISSUE_TEMPLATE/04_feature_request.yml @@ -13,19 +13,23 @@ body: description: Which Lucide project do you wish this feature were added to? You may select more than one. options: - label: lucide - - label: lucide-angular - - label: lucide-flutter - - label: lucide-preact - label: lucide-react + - label: '@lucide/vue' + - label: '@lucide/svelte' + - label: '@lucide/angular' + - label: '@lucide/astro' - label: lucide-react-native + - label: lucide-preact - label: lucide-solid - - label: lucide-svelte - - label: lucide-vue - - label: lucide-vue-next - - label: lucide-astro + - label: lucide-static + - label: '@lucide/icons' + - label: lucide-vue-next (deprecated) + - label: lucide-svelte (deprecated) + - label: lucide-angular (deprecated) - label: Figma plugin - label: all JS packages - label: site + - label: other/not relevant validations: required: true - type: textarea diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..055190392 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,8 @@ +## Reviewing PRs + +When reviewing PRs, please respond in a friendly and constructive manner. + +- First, thank the contributor for their contribution in an enthusiastic way. +- If you have any suggestions for improvement, provide them clearly and concisely. +- If you have any questions about the PR, ask them respectfully. +- Let the contributor know that we have many PRs to review and that we appreciate their patience while we review their contribution. diff --git a/.github/instructions/metadata.instructions.md b/.github/instructions/metadata.instructions.md new file mode 100644 index 000000000..9e4493442 --- /dev/null +++ b/.github/instructions/metadata.instructions.md @@ -0,0 +1,20 @@ +--- +applyTo: "icons/*.json" +--- +# JSON Metadata Descriptor + +The schema for the JSON metadata descriptor can be found in `icon.schema.json`. It defines the required and optional properties for the JSON files that accompany each icon. The JSON metadata descriptor should be placed in the same directory as the SVG file of the icon and should have the same name as the SVG file, but with a `.json` extension. For example, if the SVG file is named `home.svg`, the JSON metadata descriptor should be named `home.json`. + +## Contributors + +The `contributors` property is a required array of GitHub usernames for the people who created or contributed to the icon. It is used to give credit to contributors and to track the icon's history. Add the PR author to the `contributors` array if they are not already listed and they have made a significant contribution to the icon's SVG. + +## Tags + +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. + +# Categories + +The `categories` property is an array of strings that specify the categories to which the icon belongs, such as "devices", "interface", "media", etc. See the `categories` property in the `icon.schema.json` for more details on the allowed values. The categories should be chosen based on the use cases provided in the PR description and the existing categories in the repository. The categories should be relevant to the icon and should help users find the icon when searching for specific types of icons. The name of icon should not be included in the categories, as it is already specified. Suggest categories based on the name of the icon and the use cases provided in the PR description. Use the existing categories in the repository as a reference for consistency and to avoid duplicates. Categories should always be in lowercase and should not contain spaces. diff --git a/.github/labeler.yml b/.github/labeler.yml index b690d1926..aeb274b50 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -36,63 +36,62 @@ - any-glob-to-any-file: - 'packages/lucide/*' -# For changes in the lucide React package +# For changes in the Lucide React package ⚛️ react package: - changed-files: - any-glob-to-any-file: - 'packages/lucide-react/*' -# For changes in the lucide React Native package +# For changes in the Lucide React Native package ⚛️ react native package: - changed-files: - any-glob-to-any-file: - 'packages/lucide-react-native/*' -# For changes in the lucide vue packages +# For changes in the Lucide vue packages 💎 vue package: - changed-files: - any-glob-to-any-file: - - 'packages/lucide-vue/*' - - 'packages/lucide-vue-next/*' + - 'packages/vue/*' -# For changes in the lucide angular package +# For changes in the Lucide angular package 🅰️ angular package: - changed-files: - any-glob-to-any-file: - - 'packages/lucide-angular/*' + - 'packages/angular/*' -# For changes in the lucide preact package +# For changes in the Lucide preact package ⚛️ preact package: - changed-files: - any-glob-to-any-file: - 'packages/lucide-preact/*' -# For changes in the lucide svelte package +# For changes in the Lucide svelte package 🧣 svelte package: - changed-files: - any-glob-to-any-file: - - 'packages/lucide-svelte/*' + - 'packages/svelte/*' -# For changes in the lucide solid package +# For changes in the Lucide solid package 🪝 solid package: - changed-files: - any-glob-to-any-file: - 'packages/lucide-solid/*' -# For changes in the lucide astro package +# For changes in the Lucide astro package 🚀 astro package: - changed-files: - any-glob-to-any-file: - 'packages/astro/*' -# For changes in the lucide static package +# For changes in the @lucide/icons package +❇️ lucide-icons: + - changed-files: + - any-glob-to-any-file: + - 'packages/lucide-icons/*' + +# For changes in the Lucide static package 🪨 static package: - changed-files: - any-glob-to-any-file: - 'packages/lucide-static/*' - -# For changes in the lucide flutter package -🏹 flutter package: - - changed-files: - - any-glob-to-any-file: - - 'packages/lucide-flutter/*' diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 7efa7e6d6..483e7ce3f 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -41,13 +41,13 @@ Common scopes: icons, docs, studio, site, dev ### Naming -- [ ] I've read and followed the [naming conventions](https://lucide.dev/guide/design/icon-design-guide#naming-conventions) +- [ ] I've read and followed the [naming conventions](https://lucide.dev/contribute/icon-design-guide#naming-conventions) - [ ] I've named icons by what they are rather than their use case. - [ ] I've provided meta JSON files in `icons/[iconName].json`. ### Design -- [ ] I've read and followed the [icon design guidelines](https://lucide.dev/guide/design/icon-design-guide) +- [ ] I've read and followed the [icon design guidelines](https://lucide.dev/contribute/icon-design-guide) - [ ] 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. diff --git a/.github/workflows/lucide-angular.yml b/.github/workflows/angular.yml similarity index 85% rename from .github/workflows/lucide-angular.yml rename to .github/workflows/angular.yml index 0812c0ea4..64ec0c903 100644 --- a/.github/workflows/lucide-angular.yml +++ b/.github/workflows/angular.yml @@ -3,7 +3,7 @@ name: Lucide Angular checks on: pull_request: paths: - - packages/lucide-angular/** + - packages/angular/** - tools/build-icons/** - pnpm-lock.yaml @@ -22,7 +22,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Build - run: pnpm --filter lucide-angular build + run: pnpm --filter @lucide/angular build test: runs-on: ubuntu-latest @@ -38,4 +38,4 @@ jobs: run: pnpm install --frozen-lockfile - name: Test - run: pnpm --filter lucide-angular test + run: pnpm --filter @lucide/angular test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 072118298..0fe620531 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,16 @@ on: version: description: Version required: true + tag: + required: true + description: NPM tag + default: 'latest' + type: choice + options: + - latest + - next + - beta + - alpha permissions: id-token: write # Required for OIDC @@ -22,8 +32,37 @@ concurrency: group: continuous-integration jobs: + check-dispatch-gate: + if: github.repository == 'lucide-icons/lucide' && + startsWith(github.event.head_commit.message, 'feat(icons)') && + github.event_name != 'workflow_dispatch' + runs-on: ubuntu-latest + outputs: + RELEASE_ENVIRONMENT: ${{ steps.environment.outputs.RELEASE_ENVIRONMENT }} + steps: + - uses: actions/checkout@v6 + + - name: Fetch tags + run: git fetch --all --tags + + - name: Check if latest tag was created today + id: environment + run: | + LATEST_TAG=$(git tag --sort=-v:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1) + TAG_DATE=$(git log -1 --format=%cs "$LATEST_TAG") + TODAY=$(date +%Y-%m-%d) + + if [ "$TAG_DATE" == "$TODAY" ]; then + echo "::warning::A release tag already exists for today ($TODAY): $LATEST_TAG. Manual approval will be required to proceed." + echo "RELEASE_ENVIRONMENT=Icon Release Gated" >> $GITHUB_OUTPUT + else + echo "RELEASE_ENVIRONMENT=Icon Release" >> $GITHUB_OUTPUT + fi + create-release: - if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)') && github.event_name != 'workflow_dispatch' + if: github.repository == 'lucide-icons/lucide' + needs: check-dispatch-gate + environment: ${{ needs.check-dispatch-gate.outputs.RELEASE_ENVIRONMENT }} runs-on: ubuntu-latest outputs: VERSION: ${{ steps.new-version.outputs.NEW_VERSION }} @@ -43,7 +82,7 @@ jobs: - name: Get latest tag id: latest-tag - run: echo "LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_OUTPUT + run: echo "LATEST_TAG=$(git tag --sort=-v:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1)" >> $GITHUB_OUTPUT - name: Log latest tag run: echo '${{ steps.latest-tag.outputs.LATEST_TAG }}' @@ -85,3 +124,4 @@ jobs: secrets: inherit with: version: ${{ inputs.version }} + tag: ${{ inputs.tag }} diff --git a/.github/workflows/close-issue-with-banned-phrases.yml b/.github/workflows/close-issue-with-banned-phrases.yml index 2b74a1900..e6958de12 100644 --- a/.github/workflows/close-issue-with-banned-phrases.yml +++ b/.github/workflows/close-issue-with-banned-phrases.yml @@ -33,7 +33,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}) diff --git a/.github/workflows/lucide-svelte.yml b/.github/workflows/icons.yml similarity index 84% rename from .github/workflows/lucide-svelte.yml rename to .github/workflows/icons.yml index ec4d8e8e8..42865b6b8 100644 --- a/.github/workflows/lucide-svelte.yml +++ b/.github/workflows/icons.yml @@ -1,9 +1,9 @@ -name: Lucide Svelte checks +name: Lucide Icons Checks on: pull_request: paths: - - packages/lucide-svelte/** + - packages/icons/** - packages/shared/** - tools/build-icons/** - tools/rollup-plugins/** @@ -24,7 +24,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Build - run: pnpm --filter lucide-svelte build + run: pnpm --filter @lucide/icons build test: runs-on: ubuntu-latest @@ -40,4 +40,4 @@ jobs: run: pnpm install --frozen-lockfile - name: Test - run: pnpm --filter lucide-svelte test + run: pnpm --filter @lucide/icons test diff --git a/.github/workflows/linting-icons.yml b/.github/workflows/linting-icons.yml index fc399cd3a..1d7c48375 100644 --- a/.github/workflows/linting-icons.yml +++ b/.github/workflows/linting-icons.yml @@ -1,6 +1,9 @@ name: Linting Icons on: + push: + branches: + - next pull_request: paths: - 'icons/*' diff --git a/.github/workflows/lucide-astro.yml b/.github/workflows/lucide-astro.yml index 74b9043c3..09bcb19b8 100644 --- a/.github/workflows/lucide-astro.yml +++ b/.github/workflows/lucide-astro.yml @@ -1,6 +1,9 @@ name: Lucide Astro Checks on: + push: + branches: + - next pull_request: paths: - packages/astro/** diff --git a/.github/workflows/lucide-font.yml b/.github/workflows/lucide-font.yml index 9f76aece8..6842a01ce 100644 --- a/.github/workflows/lucide-font.yml +++ b/.github/workflows/lucide-font.yml @@ -1,6 +1,9 @@ name: Lucide font checks on: + push: + branches: + - next pull_request: paths: - tools/build-font/** @@ -20,6 +23,9 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Execute unit tests for build-font + run: pnpm test:font + - name: Create font in ./lucide-font run: pnpm build:font diff --git a/.github/workflows/lucide-preact.yml b/.github/workflows/lucide-preact.yml index 1da3cf236..fc7b1c824 100644 --- a/.github/workflows/lucide-preact.yml +++ b/.github/workflows/lucide-preact.yml @@ -1,6 +1,9 @@ name: Lucide Preact Checks on: + push: + branches: + - next pull_request: paths: - packages/lucide-preact/** diff --git a/.github/workflows/lucide-react-native.yml b/.github/workflows/lucide-react-native.yml index 7ed5cc039..99436b33e 100644 --- a/.github/workflows/lucide-react-native.yml +++ b/.github/workflows/lucide-react-native.yml @@ -1,6 +1,9 @@ name: Lucide React Native checks on: + push: + branches: + - next pull_request: paths: - packages/lucide-react-native/** diff --git a/.github/workflows/lucide-react.yml b/.github/workflows/lucide-react.yml index 2fe551f81..77f85f7d0 100644 --- a/.github/workflows/lucide-react.yml +++ b/.github/workflows/lucide-react.yml @@ -1,6 +1,9 @@ name: Lucide React Checks on: + push: + branches: + - next pull_request: paths: - packages/lucide-react/** diff --git a/.github/workflows/lucide-shared.yml b/.github/workflows/lucide-shared.yml index 69e7268db..56c4b1931 100644 --- a/.github/workflows/lucide-shared.yml +++ b/.github/workflows/lucide-shared.yml @@ -1,6 +1,9 @@ name: Lucide Shared Checks on: + push: + branches: + - next pull_request: paths: - packages/shared/** diff --git a/.github/workflows/lucide-solid.yml b/.github/workflows/lucide-solid.yml index 9c5e07424..a8515c761 100644 --- a/.github/workflows/lucide-solid.yml +++ b/.github/workflows/lucide-solid.yml @@ -1,6 +1,9 @@ name: Lucide Solid Checks on: + push: + branches: + - next pull_request: paths: - packages/lucide-solid/** diff --git a/.github/workflows/lucide-svelte-5.yml b/.github/workflows/lucide-svelte-5.yml index 556f44bfb..84f3a415f 100644 --- a/.github/workflows/lucide-svelte-5.yml +++ b/.github/workflows/lucide-svelte-5.yml @@ -1,6 +1,9 @@ name: Lucide Svelte 5 checks on: + push: + branches: + - next pull_request: paths: - packages/svelte/** diff --git a/.github/workflows/lucide-vue-next.yml b/.github/workflows/lucide-vue.yml similarity index 68% rename from .github/workflows/lucide-vue-next.yml rename to .github/workflows/lucide-vue.yml index eb2ff1663..c653429bb 100644 --- a/.github/workflows/lucide-vue-next.yml +++ b/.github/workflows/lucide-vue.yml @@ -1,9 +1,12 @@ -name: Lucide Vue Next checks +name: Lucide Vue checks on: + push: + branches: + - next pull_request: paths: - - packages/lucide-vue-next/** + - packages/lucide-vue/** - packages/shared/** - tools/build-icons/** - tools/rollup-plugins/** @@ -13,9 +16,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v6 + - uses: actions/setup-node@v4 with: cache: 'pnpm' node-version-file: 'package.json' @@ -24,14 +27,14 @@ jobs: run: pnpm install --frozen-lockfile - name: Build - run: pnpm --filter lucide-vue-next build + run: pnpm --filter @lucide/vue build test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v6 + - uses: actions/setup-node@v4 with: cache: 'pnpm' node-version-file: 'package.json' @@ -40,4 +43,4 @@ jobs: run: pnpm install --frozen-lockfile - name: Test - run: pnpm --filter lucide-vue-next test + run: pnpm --filter @lucide/vue test diff --git a/.github/workflows/lucide.yml b/.github/workflows/lucide.yml index 57fe6947d..578b71f36 100644 --- a/.github/workflows/lucide.yml +++ b/.github/workflows/lucide.yml @@ -1,6 +1,9 @@ name: Lucide checks on: + push: + branches: + - next pull_request: paths: - packages/lucide/** diff --git a/.github/workflows/pull-request-tags-suggestions.yml b/.github/workflows/pull-request-metadata-suggestions.yml similarity index 84% rename from .github/workflows/pull-request-tags-suggestions.yml rename to .github/workflows/pull-request-metadata-suggestions.yml index 141998cac..a6b2acfd2 100644 --- a/.github/workflows/pull-request-tags-suggestions.yml +++ b/.github/workflows/pull-request-metadata-suggestions.yml @@ -1,4 +1,4 @@ -name: Pull request tags suggestions +name: Pull request metadata suggestions on: pull_request_target: @@ -6,8 +6,8 @@ on: - 'icons/*.json' jobs: - pull-request-tags-suggestions: - name: Pull Request Tags Suggestions + pull-request-metadata-suggestions: + name: Pull Request Metadata Suggestions runs-on: ubuntu-latest permissions: contents: read @@ -29,7 +29,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Generate comment markup - run: node ./scripts/suggestTags.mts + run: node ./scripts/suggestMetaData.mts env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} PULL_REQUEST_NUMBER: ${{ github.event.number }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3f4898ac2..e72e488db 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,16 +11,17 @@ on: required: true description: Version type: string + tag: + description: NPM tag + default: 'latest' + type: string permissions: id-token: write # Required for OIDC contents: write -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - jobs: - pre-release: + prepare: if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere", "jguddas"]', github.actor) runs-on: ubuntu-latest outputs: @@ -38,7 +39,7 @@ jobs: release: if: github.repository == 'lucide-icons/lucide' runs-on: ubuntu-latest - needs: pre-release + needs: prepare permissions: id-token: write # Required for OIDC contents: read @@ -50,13 +51,13 @@ jobs: 'lucide', 'lucide-react', 'lucide-react-native', - 'lucide-vue-next', - 'lucide-angular', 'lucide-preact', 'lucide-solid', - 'lucide-svelte', + '@lucide/angular', '@lucide/astro', + '@lucide/icons', '@lucide/svelte', + '@lucide/vue', ] steps: - uses: actions/checkout@v6 @@ -70,7 +71,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Set new version - run: pnpm --filter ${{ matrix.package }} version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version + run: pnpm version ${{ needs.prepare.outputs.VERSION }} --filter ${{ matrix.package }} --fail-if-no-match --no-git-tag-version - name: Build run: pnpm --filter ${{ matrix.package }} build @@ -79,14 +80,14 @@ jobs: run: pnpm --filter ${{ matrix.package }} test - name: Publish - run: pnpm --filter ${{ matrix.package }} publish --access public --no-git-checks --ignore-scripts + run: pnpm --filter ${{ matrix.package }} publish --access public --no-git-checks --ignore-scripts --tag ${{ inputs.tag }} env: NPM_CONFIG_PROVENANCE: true lucide-static: if: github.repository == 'lucide-icons/lucide' runs-on: ubuntu-latest - needs: [pre-release, lucide-font] + needs: [prepare, lucide-font] permissions: id-token: write # Required for OIDC contents: read @@ -104,7 +105,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Set new version - run: pnpm --filter lucide-static version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version + run: pnpm version ${{ needs.prepare.outputs.VERSION }} --filter lucide-static --no-git-tag-version - name: Move Font run: cp -r lucide-font packages/lucide-static/font @@ -113,14 +114,14 @@ jobs: run: pnpm --filter lucide-static build - name: Publish - run: pnpm --filter lucide-static publish --no-git-checks + run: pnpm --filter lucide-static publish --no-git-checks --ignore-scripts --tag ${{ inputs.tag }} --access public env: NPM_CONFIG_PROVENANCE: true lucide-font: if: github.repository == 'lucide-icons/lucide' runs-on: ubuntu-latest - needs: pre-release + needs: prepare steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v4 @@ -132,8 +133,13 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Execute unit tests for build-font + run: pnpm test:font + - name: Create font in ./lucide-font run: pnpm build:font --saveCodePoints + env: + BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }} - name: 'Upload to Artifacts' uses: actions/upload-artifact@v4 @@ -142,9 +148,9 @@ jobs: path: lucide-font post-release: - if: github.repository == 'lucide-icons/lucide' + if: github.repository == 'lucide-icons/lucide' && github.event_name != 'workflow_call' runs-on: ubuntu-latest - needs: [pre-release, lucide-font] + needs: [prepare, lucide-font] permissions: id-token: write # Required for OIDC contents: write @@ -153,13 +159,14 @@ jobs: - uses: actions/download-artifact@v4 - name: Zip font and icons run: | - zip -r lucide-font-${{ needs.pre-release.outputs.VERSION }}.zip lucide-font - zip -r lucide-icons-${{ needs.pre-release.outputs.VERSION }}.zip icons + 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 with: - tag_name: ${{ needs.pre-release.outputs.VERSION }} + tag_name: ${{ needs.prepare.outputs.VERSION }} + files: | - lucide-font-${{ needs.pre-release.outputs.VERSION }}.zip - lucide-icons-${{ needs.pre-release.outputs.VERSION }}.zip + lucide-font-${{ needs.prepare.outputs.VERSION }}.zip + lucide-icons-${{ needs.prepare.outputs.VERSION }}.zip diff --git a/.github/workflows/repo-journal.yml b/.github/workflows/repo-journal.yml new file mode 100644 index 000000000..09638bac5 --- /dev/null +++ b/.github/workflows/repo-journal.yml @@ -0,0 +1,42 @@ +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: + runs-on: ubuntu-latest + steps: + - name: Set up Node.js environment + uses: actions/setup-node@v4 + 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" diff --git a/.gitignore b/.gitignore index 2112bf699..2e7bd7183 100644 --- a/.gitignore +++ b/.gitignore @@ -26,15 +26,20 @@ packages/**/src/utils/*.ts packages/**/src/dynamicIconImports.ts packages/**/DynamicIcon.d.ts packages/**/dynamicIconImports.js +packages/**/dynamicIconImports.mjs packages/**/dynamicIconImports.d.ts +packages/**/dynamicIconImports.d.mts packages/**/dynamicIconImports.js.map +packages/**/dynamicIconImports.mjs.map packages/**/dynamic.d.ts +packages/**/dynamic.d.mts packages/**/dynamic.mjs.map packages/**/dynamic.mjs packages/**/LICENSE categories.json tags.json .vercel +package-lock.json # docs docs/.vitepress/cache @@ -48,6 +53,9 @@ docs/.vitepress/data/categoriesData.json docs/.vitepress/data/iconDetails docs/.vitepress/data/relatedIcons.json docs/.vitepress/data/brandStopwords.json +docs/.vitepress/data/iconPopularity docs/.vercel docs/.nitro +docs/public/og/** +!docs/public/og/general.png .gitignore diff --git a/.prettierignore b/.prettierignore index c150f271b..435726b81 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,8 +7,8 @@ docs/.vitepress/cache docs/.vitepress/data docs/.nitro -# lucide-angular -packages/lucide-angular/.angular/cache +# @lucide/angular +packages/angular/.angular # lucide-static packages/lucide-static/icons @@ -17,7 +17,8 @@ packages/lucide-static/sprite.svg packages/lucide-static/tags.json packages/lucide-static/icon-nodes.json packages/lucide-static/font +packages/svelte/.svelte-kit # lucide-svelte -packages/lucide-svelte/src/icons/*.svelte -packages/lucide-svelte/.svelte-kit +packages/svelte/src/icons/*.svelte +packages/svelte/.svelte-kit diff --git a/.vscode/settings.json b/.vscode/settings.json index f179cbc07..47728fdf7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,5 @@ "cSpell.words": ["devs", "preact", "Preact"], "eslint.enable": true, "eslint.validate": ["javascript", "svg"], - "svg.preview.background": "transparent" + "svg.preview.background": "editor" } diff --git a/BRAND_LOGOS_STATEMENT.md b/BRAND_LOGOS_STATEMENT.md index 9ca7c149b..6a7404c79 100644 --- a/BRAND_LOGOS_STATEMENT.md +++ b/BRAND_LOGOS_STATEMENT.md @@ -1,4 +1,6 @@ -# Our Official Stance on Including Brand Logos in Lucide +# Brand Logos Statement + +Our official stance on including brand logos in Lucide ## TL;DR @@ -37,7 +39,7 @@ This means adding them could: Lucide is all about **visual consistency**. Adding brand logos would: -- Break [our own design rules](https://lucide.dev/guide/design/icon-design-guide#icon-design-principles) for shapes, proportions, and stroke. +- Break [our own design rules](https://lucide.dev/contribute/icon-design-guide#icon-design-principles) for shapes, proportions, and stroke. - Mix two fundamentally different categories of graphics (pictograms vs. corporate logos). - Create a library where a subset of icons will always look "out of place". diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index bd8e14ad0..da613c8d4 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,74 +1,90 @@ # Contributor Covenant Code of Conduct +This Code of Conduct outlines our expectations for participant behavior as well as the consequences for unacceptable behavior within our community. We are committed to providing a welcoming, safe, and inclusive environment for all members of our community. + ## Our Pledge -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. +We pledge to make our community welcoming, safe, and equitable for all. -## Our Standards +We are committed to fostering an environment that respects and promotes the dignity, rights, and contributions of all individuals, regardless of characteristics including race, ethnicity, caste, color, age, physical characteristics, neurodiversity, disability, sex or gender, gender identity or expression, sexual orientation, language, philosophy or religion, national or social origin, socio-economic position, level of education, or other status. The same privileges of participation are extended to everyone who participates in good faith and in accordance with this Covenant. -Examples of behavior that contributes to creating a positive environment -include: +## Encouraged Behaviors -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +While acknowledging differences in social norms, we all strive to meet our community's expectations for positive behavior. We also understand that our words and actions may be interpreted differently than we intend based on culture, background, or native language. -Examples of unacceptable behavior by participants include: +With these considerations in mind, we agree to behave mindfully toward each other and act in ways that center our shared values, including: -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +1. Respecting the **purpose of our community**, our activities, and our ways of gathering. +2. Engaging **kindly and honestly** with others. +3. Respecting **different viewpoints** and experiences. +4. **Taking responsibility** for our actions and contributions. +5. Gracefully giving and accepting **constructive feedback**. +6. Committing to **repairing harm** when it occurs. +7. Behaving in other ways that promote and sustain the **well-being of our community**. -## Our Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. +## Restricted Behaviors + +We agree to restrict the following behaviors in our community. Instances, threats, and promotion of these behaviors are violations of this Code of Conduct. + +1. **Harassment.** Violating explicitly expressed boundaries or engaging in unnecessary personal attention after any clear request to stop. +2. **Character attacks.** Making insulting, demeaning, or pejorative comments directed at a community member or group of people. +3. **Stereotyping or discrimination.** Characterizing anyone’s personality or behavior on the basis of immutable identities or traits. +4. **Sexualization.** Behaving in a way that would generally be considered inappropriately intimate in the context or purpose of the community. +5. **Violating confidentiality**. Sharing or acting on someone's personal or private information without their permission. +6. **Endangerment.** Causing, encouraging, or threatening violence or other harm toward any person or group. +7. Behaving in other ways that **threaten the well-being** of our community. + +### Other Restrictions + +1. **Misleading identity.** Impersonating someone else for any reason, or pretending to be someone else to evade enforcement actions. +2. **Failing to credit sources.** Not properly crediting the sources of content you contribute. +3. **Promotional materials**. Sharing marketing or other commercial content in a way that is outside the norms of the community. +4. **Irresponsible communication.** Failing to responsibly present content which includes, links or describes any other restricted behaviors. + + +## Reporting an Issue + +Tensions can occur between community members even when they are trying their best to collaborate. Not every conflict represents a code of conduct violation, and this Code of Conduct reinforces encouraged behaviors and norms that can help avoid conflicts and minimize harm. + +When an incident does occur, it is important to report it promptly. To report a possible violation, email: [info@lucide.dev](mailto:info@lucide.dev) + +Community Moderators take reports of violations seriously and will make every effort to respond in a timely manner. They will investigate all reports of code of conduct violations, reviewing messages, logs, and recordings, or interviewing witnesses and other participants. Community Moderators will keep investigation and enforcement actions as transparent as possible while prioritizing safety and confidentiality. In order to honor these values, enforcement actions are carried out in private with the involved parties, but communicating to the whole community may be part of a mutually agreed upon resolution. + + +## Addressing and Repairing Harm + +If an investigation by the Community Moderators finds that this Code of Conduct has been violated, the following enforcement ladder may be used to determine how best to repair harm, based on the incident's impact on the individuals involved and the community as a whole. Depending on the severity of a violation, lower rungs on the ladder may be skipped. + +1) Warning + 1) Event: A violation involving a single incident or series of incidents. + 2) Consequence: A private, written warning from the Community Moderators. + 3) Repair: Examples of repair include a private written apology, acknowledgement of responsibility, and seeking clarification on expectations. +2) Temporarily Limited Activities + 1) Event: A repeated incidence of a violation that previously resulted in a warning, or the first incidence of a more serious violation. + 2) Consequence: A private, written warning with a time-limited cooldown period designed to underscore the seriousness of the situation and give the community members involved time to process the incident. The cooldown period may be limited to particular communication channels or interactions with particular community members. + 3) Repair: Examples of repair may include making an apology, using the cooldown period to reflect on actions and impact, and being thoughtful about re-entering community spaces after the period is over. +3) Temporary Suspension + 1) Event: A pattern of repeated violation which the Community Moderators have tried to address with warnings, or a single serious violation. + 2) Consequence: A private written warning with conditions for return from suspension. In general, temporary suspensions give the person being suspended time to reflect upon their behavior and possible corrective actions. + 3) Repair: Examples of repair include respecting the spirit of the suspension, meeting the specified conditions for return, and being thoughtful about how to reintegrate with the community when the suspension is lifted. +4) Permanent Ban + 1) Event: A pattern of repeated code of conduct violations that other steps on the ladder have failed to resolve, or a violation so serious that the Community Moderators determine there is no way to keep the community safe with this person as a member. + 2) Consequence: Access to all community spaces, tools, and communication channels is removed. In general, permanent bans should be rarely used, should have strong reasoning behind them, and should only be resorted to if working through other remedies has failed to change the behavior. + 3) Repair: There is no possible repair in cases of this severity. + +This enforcement ladder is intended as a guideline. It does not limit the ability of Community Managers to use their discretion and judgment, in keeping with the best interests of our community. -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. ## Scope -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public or other spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event. -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at johnletey@gmail.com. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] +This Code of Conduct is adapted from the Contributor Covenant, version 3.0, permanently available at [https://www.contributor-covenant.org/version/3/0/](https://www.contributor-covenant.org/version/3/0/). -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +Contributor Covenant is stewarded by the Organization for Ethical Source and licensed under CC BY-SA 4.0. To view a copy of this license, visit [https://creativecommons.org/licenses/by-sa/4.0/](https://creativecommons.org/licenses/by-sa/4.0/) + +For answers to common questions about Contributor Covenant, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are provided at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). Additional enforcement and community guideline resources can be found at [https://www.contributor-covenant.org/resources](https://www.contributor-covenant.org/resources). The enforcement ladder was inspired by the work of [Mozilla’s code of conduct team](https://github.com/mozilla/inclusion). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e7bb31b0f..8ffc4992c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,4 @@ - -# Contribution Guidelines +# Contribution Guide :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: @@ -19,13 +18,15 @@ Guidelines for pull requests: - __Make sure the target of your pull request is the relevant branch__. Most of bug fixes or new feature should go to the `main` branch. - __Include only related work__. If your pull request has unrelated commits, it won't be accepted. -### Icon Pull Requests +### Contributing Icons + +We love contributions of new icons from the community! If you want to contribute new icons, please follow the guidelines below. #### Guidelines Please make sure you follow the icon guidelines, that should be followed to keep quality and consistency when making icons for Lucide. -Read it here: [ICON_GUIDELINES](https://lucide.dev/docs/icon-design-guide). +Read it here: [ICON_GUIDELINES](https://lucide.dev/contribute/icon-design-guide). #### Lucide Studio @@ -35,15 +36,15 @@ For formatting and adjusting SVG icons, [@jguddas](https://github.com/jguddas) m Here you can find instructions on how to implement the guidelines with different vector graphics editors: -##### [Adobe Illustrator Guide](https://lucide.dev/docs/illustrator-guide) +##### [Adobe Illustrator Guide](https://lucide.dev/contribute/illustrator-guide) -You can also [download an Adobe Illustrator template](https://github.com/lucide-icons/lucide/blob/main/docs/public/templates/illustrator_template.ai). +You can also [download an Adobe Illustrator template](https://lucide.dev/templates/illustrator_template.ai). -##### [Inkscape Guide](https://lucide.dev/docs/inkscape-guide) +##### [Inkscape Guide](https://lucide.dev/contribute/inkscape-guide) -##### [Figma Guide](https://lucide.dev/docs/figma-guide) +##### [Figma Guide](https://lucide.dev/contribute/figma-guide) -##### [Affinity Designer Guide](https://lucide.dev/guide/design/affinity-designer-guide) +##### [Affinity Designer Guide](https://lucide.dev/contribute/affinity-designer-guide) #### Submitting Multiple Icons @@ -191,7 +192,7 @@ Detailed documentation about: installation, guides, packages, design guides etc. ### Icons -All the icons of lucide in SVG format. These will be used as source for all the packages and other distributions for the lucide icons. +All the icons of Lucide in SVG format. These will be used as source for all the packages and other distributions for the Lucide icons. ### Packages diff --git a/LICENSE b/LICENSE index 322e0d86e..718bb3f0e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ ISC License -Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2026 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2026. +Copyright (c) 2026 Lucide Icons and Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -16,9 +16,13 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. --- -The MIT License (MIT) (for portions derived from Feather) +The following Lucide icons are derived from the Feather project: -Copyright (c) 2013-2026 Cole Bemis +airplay, alert-circle, alert-octagon, alert-triangle, aperture, arrow-down-circle, arrow-down-left, arrow-down-right, arrow-down, arrow-left-circle, arrow-left, arrow-right-circle, arrow-right, arrow-up-circle, arrow-up-left, arrow-up-right, arrow-up, at-sign, calendar, cast, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down, chevrons-left, chevrons-right, chevrons-up, circle, clipboard, clock, code, columns, command, compass, corner-down-left, corner-down-right, corner-left-down, corner-left-up, corner-right-down, corner-right-up, corner-up-left, corner-up-right, crosshair, database, divide-circle, divide-square, dollar-sign, download, external-link, feather, frown, hash, headphones, help-circle, info, italic, key, layout, life-buoy, link-2, link, loader, lock, log-in, log-out, maximize, meh, minimize, minimize-2, minus-circle, minus-square, minus, monitor, moon, more-horizontal, more-vertical, move, music, navigation-2, navigation, octagon, pause-circle, percent, plus-circle, plus-square, plus, power, radio, rss, search, server, share, shopping-bag, sidebar, smartphone, smile, square, table-2, tablet, target, terminal, trash-2, trash, triangle, tv, type, upload, x-circle, x-octagon, x-square, x, zoom-in, zoom-out + +The MIT License (MIT) (for the icons listed above) + +Copyright (c) 2013-present Cole Bemis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ab0c51018..a33e9026d 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@

- license + license figma installs - build status + build status discord chat

@@ -26,26 +26,26 @@ # Lucide -Lucide is an open-source icon library that provides 1000+ vector (svg) files for displaying icons and symbols in digital and non-digital projects. The library aims to make it easier for designers and developers to incorporate icons into their projects by providing several official [packages](https://lucide.dev/packages) to make it easier to use these icons in your project. +Lucide is an open-source icon library that provides 1600+ vector (svg) files for displaying icons and symbols in digital and non-digital projects. The library aims to make it easier for designers and developers to incorporate icons into their projects by providing several official [packages](https://lucide.dev/packages) to make it easier to use these icons in your project. ## Packages | Logo | Package | Version | Downloads | Links | | ---- | ------- | ------- | --------- | ----- | -| JS logo | **`lucide`** | [![npm](https://img.shields.io/npm/v/lucide)](https://www.npmjs.com/package/lucide) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide) | [Docs](https://lucide.dev/guide/packages/lucide) · [Source](./packages/lucide) | -| React logo | **`lucide-react`** | [![npm](https://img.shields.io/npm/v/lucide-react)](https://www.npmjs.com/package/lucide-react) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-react) | [Docs](https://lucide.dev/guide/packages/lucide-react) · [Source](./packages/lucide-react) | -| Vue logo | **`lucide-vue-next`** | [![npm](https://img.shields.io/npm/v/lucide-vue-next)](https://www.npmjs.com/package/lucide-vue-next) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-vue-next) | [Docs](https://lucide.dev/guide/packages/lucide-vue-next) · [Source](./packages/lucide-vue-next) | -| Svelte logo | **`lucide-svelte`** | [![npm](https://img.shields.io/npm/v/lucide-svelte)](https://www.npmjs.com/package/lucide-svelte) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-svelte) | [Docs](https://lucide.dev/guide/packages/lucide-svelte) · [Source](./packages/lucide-svelte) | -| Solid logo | **`lucide-solid`** | [![npm](https://img.shields.io/npm/v/lucide-solid)](https://www.npmjs.com/package/lucide-solid) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-solid) | [Docs](https://lucide.dev/guide/packages/lucide-solid) · [Source](./packages/lucide-solid) | -| Preact logo | **`lucide-preact`** | [![npm](https://img.shields.io/npm/v/lucide-preact)](https://www.npmjs.com/package/lucide-preact) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-preact) | [Docs](https://lucide.dev/guide/packages/lucide-preact) · [Source](./packages/lucide-preact) | -| React Native logo | **`lucide-react-native`** | [![npm](https://img.shields.io/npm/v/lucide-react-native)](https://www.npmjs.com/package/lucide-react-native) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-react-native) | [Docs](https://lucide.dev/guide/packages/lucide-react-native) · [Source](./packages/lucide-react-native) | -| Angular logo | **`lucide-angular`** | [![npm](https://img.shields.io/npm/v/lucide-angular)](https://www.npmjs.com/package/lucide-angular) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-angular) | [Docs](https://lucide.dev/guide/packages/lucide-angular) · [Source](./packages/lucide-angular) | -| Astro logo | **`@lucide/astro`** | [![npm](https://img.shields.io/npm/v/@lucide/astro)](https://www.npmjs.com/package/@lucide/astro) | ![NPM Downloads](https://img.shields.io/npm/dw/@lucide/astro) | [Docs](https://lucide.dev/guide/packages/lucide-astro) · [Source](./packages/astro) | -| SVG logo | **`lucide-static`** | [![npm](https://img.shields.io/npm/v/lucide-static)](https://www.npmjs.com/package/lucide-static) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-static) | [Docs](https://lucide.dev/guide/packages/lucide-static) · [Source](./packages/lucide-static) | +| JS logo | **`lucide`** | [![npm](https://img.shields.io/npm/v/lucide)](https://www.npmjs.com/package/lucide) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide) | [Docs](https://lucide.dev/guide/lucide) · [Source](./packages/lucide) | +| React logo | **`lucide-react`** | [![npm](https://img.shields.io/npm/v/lucide-react)](https://www.npmjs.com/package/lucide-react) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-react) | [Docs](https://lucide.dev/guide/react) · [Source](./packages/lucide-react) | +| Vue logo | **`@lucide/vue`** | [![npm](https://img.shields.io/npm/v/@lucide/vue)](https://www.npmjs.com/package/@lucide/vue) | ![NPM Downloads](https://img.shields.io/npm/dw/@lucide/vue) | [Docs](https://lucide.dev/guide/vue) · [Source](./packages/vue) | +| Svelte logo | **`@lucide/svelte`** | [![npm](https://img.shields.io/npm/v/@lucide/svelte)](https://www.npmjs.com/package/@lucide/svelte) | ![NPM Downloads](https://img.shields.io/npm/dw/@lucide/svelte) | [Docs](https://lucide.dev/guide/svelte) · [Source](./packages/svelte) | +| Solid logo | **`lucide-solid`** | [![npm](https://img.shields.io/npm/v/lucide-solid)](https://www.npmjs.com/package/lucide-solid) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-solid) | [Docs](https://lucide.dev/guide/solid) · [Source](./packages/lucide-solid) | +| Preact logo | **`lucide-preact`** | [![npm](https://img.shields.io/npm/v/lucide-preact)](https://www.npmjs.com/package/lucide-preact) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-preact) | [Docs](https://lucide.dev/guide/preact) · [Source](./packages/lucide-preact) | +| React Native logo | **`lucide-react-native`** | [![npm](https://img.shields.io/npm/v/lucide-react-native)](https://www.npmjs.com/package/lucide-react-native) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-react-native) | [Docs](https://lucide.dev/guide/react-native) · [Source](./packages/lucide-react-native) | +| Angular logo | **`@lucide/angular`** | [![npm](https://img.shields.io/npm/v/@lucide/angular)](https://www.npmjs.com/package/@lucide/angular) | ![NPM Downloads](https://img.shields.io/npm/dw/@lucide/angular) | [Docs](https://lucide.dev/guide/angular) · [Source](./packages/angular) | +| Astro logo | **`@lucide/astro`** | [![npm](https://img.shields.io/npm/v/@lucide/astro)](https://www.npmjs.com/package/@lucide/astro) | ![NPM Downloads](https://img.shields.io/npm/dw/@lucide/astro) | [Docs](https://lucide.dev/guide/astro) · [Source](./packages/astro) | +| SVG logo | **`lucide-static`** | [![npm](https://img.shields.io/npm/v/lucide-static)](https://www.npmjs.com/package/lucide-static) | ![NPM Downloads](https://img.shields.io/npm/dw/lucide-static) | [Docs](https://lucide.dev/guide/static) · [Source](./packages/lucide-static) | ### Figma -The lucide figma plugin. +The Lucide Figma plugin. Visit [Figma community page](https://www.figma.com/community/plugin/939567362549682242/Lucide-Icons) to install the plugin. @@ -80,6 +80,8 @@ Thank you to all the people who contributed to Lucide! +[//]: <> (Sponsors) + ## Sponsors @@ -88,7 +90,10 @@ Thank you to all the people who contributed to Lucide! DigitalOcean Referral Badge -[//]: <> (Open Collective backers) +### Hero backers 🦸 + +Zephyr Cloud – From idea to prod: fast micro-frontend delivery! + ### Awesome backers 🍺 pdfme – Open-source PDF generation library built with TypeScript and React. diff --git a/brand-stopwords.json b/brand-stopwords.json index 77c9ae265..b30182d5a 100644 --- a/brand-stopwords.json +++ b/brand-stopwords.json @@ -54,7 +54,6 @@ "jira": "Jira", "kotlin": "Kotlin", "kubernetes": "Kubernetes", - "less": "Less", "leetcode": "LeetCode", "leet-code": "LeetCode", "line": "LINE", diff --git a/categories/brands.json b/categories/brands.json deleted file mode 100644 index c48c4b3b0..000000000 --- a/categories/brands.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "../category.schema.json", - "title": "Brands", - "icon": "facebook" -} diff --git a/categories/navigation.json b/categories/navigation.json index 86a3e5503..0c46693cb 100644 --- a/categories/navigation.json +++ b/categories/navigation.json @@ -1,5 +1,5 @@ { "$schema": "../category.schema.json", - "title": "Navigation, Maps, and POIs", + "title": "Navigation & Places", "icon": "compass" } diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 80011e10f..12102a1a7 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,9 +1,22 @@ import { fileURLToPath, URL } from 'node:url'; -import { defineConfig } from 'vitepress'; +import { defineConfig, UserConfig } from 'vitepress'; +import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons'; import sidebar from './sidebar'; import getStructuredData from './getStructuredData'; import nitro from '@analogjs/vite-plugin-nitro'; import nitroConfig from '../nitro.config'; +import snackPlayer from './markdown/snackPlayer'; +import sandpackPlugin from './markdown/sandpack'; +import { readFile } from 'node:fs/promises'; +import { resourcesSidebar } from './sidebar/resources'; +import llmstxt from 'vitepress-plugin-llms'; +import { transformPageData } from './transformPageData'; +import getHeadConfig from './getHeadConfig'; + +const defaultSandpackCSS = await readFile( + fileURLToPath(new URL('./theme/sandpack-default.css', import.meta.url)), + 'utf-8', +); const title = 'Lucide'; const socialTitle = 'Lucide Icons'; @@ -19,6 +32,20 @@ export default defineConfig({ cleanUrls: true, outDir: '.vercel/output/static', srcExclude: ['**/README.md'], + markdown: { + config(md) { + md.use(groupIconMdPlugin); + md.use(snackPlayer); + md.use(sandpackPlugin, { + defaultFiles: { + '/styles.css': { + code: defaultSandpackCSS, + hidden: true, + }, + }, + }); + }, + }, vite: { resolve: { alias: [ @@ -34,6 +61,12 @@ export default defineConfig({ new URL('./theme/components/overrides/VPFooter.vue', import.meta.url), ), }, + { + find: /^.*\/VPCarbonAds\.vue$/, + replacement: fileURLToPath( + new URL('./theme/components/overrides/VPCarbonAds.vue', import.meta.url), + ), + }, { find: '~/.vitepress', replacement: fileURLToPath(new URL('./', import.meta.url)), @@ -53,152 +86,21 @@ export default defineConfig({ scanDirs: ['.'], }, ), + groupIconVitePlugin(), + llmstxt({ + ignoreFiles: [ + 'code-of-conduct.md', + 'index.md', + 'packages.md', + 'showcase.md', + 'brand-logo-statement.md', + 'icons/**', // Not working, need investigation + ], + }) as unknown as UserConfig['vite']['plugins'][0], ], }, - head: [ - [ - 'link', - { - rel: 'preconnect', - href: 'https://analytics.lucide.dev', - }, - ], - [ - 'script', - { - src: 'https://analytics.lucide.dev/js/script.js', - 'data-domain': 'lucide.dev', - defer: '', - }, - ], - [ - 'meta', - { - property: 'og:locale', - content: 'en_US', - }, - ], - [ - 'meta', - { - property: 'og:type', - content: 'website', - }, - ], - [ - 'meta', - { - property: 'og:site_name', - content: title, - }, - ], - [ - 'meta', - { - property: 'og:title', - content: socialTitle, - }, - ], - [ - 'meta', - { - property: 'og:description', - content: description, - }, - ], - [ - 'meta', - { - property: 'og:url', - content: 'https://lucide.dev', - }, - ], - [ - 'meta', - { - property: 'og:image', - content: 'https://lucide.dev/og.png', - }, - ], - [ - 'meta', - { - property: 'og:image:width', - content: '1200', - }, - ], - [ - 'meta', - { - property: 'og:image:height', - content: '630', - }, - ], - [ - 'meta', - { - property: 'og:image:type', - content: 'image/png', - }, - ], - [ - 'meta', - { - property: 'twitter:card', - content: 'summary_large_image', - }, - ], - [ - 'meta', - { - property: 'twitter:title', - content: socialTitle, - }, - ], - [ - 'meta', - { - property: 'twitter:description', - content: description, - }, - ], - [ - 'meta', - { - property: 'twitter:image', - content: 'https://lucide.dev/og.png', - }, - ], - ], - async transformPageData(pageData) { - if ( - pageData.relativePath.startsWith('icons/') && - !pageData.relativePath.startsWith('icons/lab/') && - pageData.params?.name - ) { - const iconName = pageData.params.name; - pageData.title = `${iconName} icon details`; - - const taggedAs = pageData.params?.tags?.length - ? `Tagged as: ${pageData.params.tags.join(', ')}.` - : ''; - const categorizedIn = pageData.params?.category?.length - ? `Categorized in: ${pageData.params.category.join(', ')}.` - : ''; - - pageData.description = - `Details and related icons for ${iconName} icon. ${taggedAs} ${categorizedIn}`.trim(); - - const structuredData = await getStructuredData(iconName, pageData); - - pageData.frontmatter.head ??= []; - pageData.frontmatter.head.push([ - 'script', - { type: 'application/ld+json' }, - JSON.stringify(structuredData), - ]); - } - }, + head: getHeadConfig({ title, description, socialTitle }), + transformPageData, themeConfig: { logo: { light: '/logo.light.svg', @@ -207,9 +109,16 @@ export default defineConfig({ nav: [ { text: 'Icons', link: '/icons/' }, { text: 'Guide', link: '/guide/' }, + { + text: 'Resources', + items: [ + ...resourcesSidebar[0].items, + { text: 'Design icons', link: '/contribute/icon-design-guide' }, + ], + }, { text: 'Packages', link: '/packages' }, { text: 'Showcase', link: '/showcase' }, - { text: 'License', link: '/license' }, + { text: 'Merch', link: 'https://merch.lucide.dev/' }, ], sidebar, socialLinks: [ @@ -218,7 +127,7 @@ export default defineConfig({ ], footer: { message: 'Released under the ISC License.', - copyright: `Copyright © ${new Date().getFullYear()} Lucide Contributors`, + copyright: `Copyright © ${new Date().getFullYear()} Lucide Icons`, }, editLink: { pattern: 'https://github.com/lucide-icons/lucide/edit/main/docs/:path', diff --git a/docs/.vitepress/createOGImage.ts b/docs/.vitepress/createOGImage.ts new file mode 100644 index 000000000..84098314c --- /dev/null +++ b/docs/.vitepress/createOGImage.ts @@ -0,0 +1,79 @@ +import { PageData } from 'vitepress'; +import fs from 'node:fs/promises'; +import { cwd } from 'node:process'; +import { join } from 'node:path'; +import sharp from 'sharp'; + +const generalSVGTemplate = fs.readFile(join(cwd(), '.vitepress/lib/guideOGTemplate.svg'), 'utf-8'); + +export async function createGeneralOGImage({ title, description, relativePath }: PageData) { + const fileName = relativePath.replace(/\.md$/, '.png').replace(/\//g, '-'); + + const path = join(cwd(), 'public/og', fileName); + + let desc = description.replace(/&/g, '&').replace(//g, '>'); + if (!desc) { + desc = ''; + } + const lines = desc.replace(/(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n').split('\n'); + const data = { + title: title, + line1: lines[0] || '', + line2: lines[1] || '', + line3: lines[2] || '', + line4: lines[3] || '', + }; + const svgTemplate = await generalSVGTemplate; + const svg = svgTemplate.replace(/\{\{([^}]+)\}\}/g, (_, name: keyof typeof data) => data[name]); + + console.log(`Generating ${fileName}`); + try { + await sharp(Buffer.from(svg)) + .resize(1200 * 1.1, 630 * 1.1) + .png() + .toFile(path); + } catch (e) { + console.error('Error generating', { filename: path, ...data, svg }); + console.error(e); + } + + return `/og/${fileName}`; +} + +const iconSVGTemplate = fs.readFile(join(cwd(), '.vitepress/lib/iconOGTemplate.svg'), 'utf-8'); + +export async function createIconOGImage(iconName: string, tags: string[]) { + const path = join(cwd(), 'public/og', `icon-${iconName}.png`); + + let iconSVG = await fs.readFile(join(cwd(), `../icons/${iconName}.svg`), 'utf-8'); + iconSVG = iconSVG + .replace('width="24"', 'width="320"') + .replace('height="24"', 'height="320"') + .replace('currentColor', '#dfdfd6') + .replace( + 'xmlns="http://www.w3.org/2000/svg"', + 'xmlns="http://www.w3.org/2000/svg" x="62" y="66"', + ); + + const data = { + name: iconName, + tags: tags.join(' • ').replace(//g, '>').replace(/&/g, '&'), + iconSVG, + }; + + const svgTemplate = await iconSVGTemplate; + const svg = svgTemplate.replace(/\{\{([^}]+)\}\}/g, (_, name: keyof typeof data) => data[name]); + + console.log(`Generating ${path}`); + try { + await sharp(Buffer.from(svg)) + .resize(1200 * 1.1, 630 * 1.1) + .png() + .toFile(path); + } catch (e) { + console.error('Error generating', { filename: path, ...data, svg }); + console.error(e); + } + + return `/og/icon-${iconName}.png`; +} diff --git a/docs/.vitepress/data/categoriesData.json b/docs/.vitepress/data/categoriesData.json index 03f5236bd..dcb8e5f07 100644 --- a/docs/.vitepress/data/categoriesData.json +++ b/docs/.vitepress/data/categoriesData.json @@ -15,10 +15,6 @@ "name": "arrows", "title": "Arrows" }, - { - "name": "brands", - "title": "Brands" - }, { "name": "buildings", "title": "Buildings" @@ -101,7 +97,7 @@ }, { "name": "navigation", - "title": "Navigation, Maps, and POIs" + "title": "Navigation & Places" }, { "name": "notifications", diff --git a/docs/.vitepress/data/packageData.json b/docs/.vitepress/data/packageData.json index 6ad333133..9dd193f59 100644 --- a/docs/.vitepress/data/packageData.json +++ b/docs/.vitepress/data/packageData.json @@ -18,6 +18,7 @@ "lucide-react": { "order": 1, "icon": "react", + "docsAlias": "react", "shields": [ { "alt": "npm", @@ -31,41 +32,46 @@ } ] }, - "lucide-vue-next": { + "@lucide/vue": { "order": 2, - "icon": "vue-next", + "icon": "vue", + "docsAlias": "vue", + "packageDirname": "vue", "shields": [ { "alt": "npm", - "src": "https://img.shields.io/npm/v/lucide-vue-next", - "href": "https://www.npmjs.com/package/lucide-vue-next" + "src": "https://img.shields.io/npm/v/@lucide/vue", + "href": "https://www.npmjs.com/package/@lucide/vue" }, { "alt": "npm", - "src": "https://img.shields.io/npm/dw/lucide-vue-next", - "href": "https://www.npmjs.com/package/lucide-vue-next" + "src": "https://img.shields.io/npm/dw/@lucide/vue", + "href": "https://www.npmjs.com/package/@lucide/vue" } ] }, - "lucide-svelte": { + "@lucide/svelte": { "order": 3, "icon": "svelte", + "docsAlias": "svelte", + "packageDirname": "svelte", "shields": [ { "alt": "npm", - "src": "https://img.shields.io/npm/v/lucide-svelte", - "href": "https://www.npmjs.com/package/lucide-svelte" + "src": "https://img.shields.io/npm/v/@lucide/svelte", + "href": "https://www.npmjs.com/package/@lucide/svelte" }, { "alt": "npm", - "src": "https://img.shields.io/npm/dw/lucide-svelte", - "href": "https://www.npmjs.com/package/lucide-svelte" + "src": "https://img.shields.io/npm/dw/@lucide/svelte", + "href": "https://www.npmjs.com/package/@lucide/svelte" } ] }, "lucide-solid": { "order": 4, "icon": "solid", + "docsAlias": "solid", "shields": [ { "alt": "npm", @@ -82,6 +88,7 @@ "lucide-react-native": { "order": 5, "icon": "react-native", + "docsAlias": "react-native", "shields": [ { "alt": "npm", @@ -95,25 +102,28 @@ } ] }, - "lucide-angular": { + "@lucide/angular": { "order": 6, "icon": "angular", + "docsAlias": "angular", + "packageDirname": "angular", "shields": [ { "alt": "npm", - "src": "https://img.shields.io/npm/v/lucide-angular", - "href": "https://www.npmjs.com/package/lucide-angular" + "src": "https://img.shields.io/npm/v/@lucide/angular", + "href": "https://www.npmjs.com/package/@lucide/angular" }, { "alt": "npm", - "src": "https://img.shields.io/npm/dw/lucide-angular", - "href": "https://www.npmjs.com/package/lucide-angular" + "src": "https://img.shields.io/npm/dw/@lucide/angular", + "href": "https://www.npmjs.com/package/@lucide/angular" } ] }, "lucide-preact": { - "order": 7, + "order": 8, "icon": "preact", + "docsAlias": "preact", "shields": [ { "alt": "npm", @@ -128,9 +138,9 @@ ] }, "@lucide/astro": { - "docsAlias": "lucide-astro", + "docsAlias": "astro", "packageDirname": "astro", - "order": 8, + "order": 9, "icon": "astro", "iconDark": "astro-dark", "shields": [ @@ -147,8 +157,9 @@ ] }, "lucide-static": { - "order": 9, + "order": 10, "icon": "svg", + "docsAlias": "static", "shields": [ { "alt": "npm", @@ -161,5 +172,24 @@ "href": "https://www.npmjs.com/package/lucide-static" } ] + }, + "@lucide/icons": { + "hide": true, + "docsAlias": "icons", + "packageDirname": "icons", + "order": 10, + "icon": "ts", + "shields": [ + { + "alt": "npm", + "src": "https://img.shields.io/npm/v/@lucide/icons", + "href": "https://www.npmjs.com/package/@lucide/icons" + }, + { + "alt": "npm", + "src": "https://img.shields.io/npm/dw/@lucide/icons", + "href": "https://www.npmjs.com/package/@lucide/icons" + } + ] } -} +} \ No newline at end of file diff --git a/docs/.vitepress/data/packageData.thirdParty.json b/docs/.vitepress/data/packageData.thirdParty.json index db39dc98d..37f44cad6 100644 --- a/docs/.vitepress/data/packageData.thirdParty.json +++ b/docs/.vitepress/data/packageData.thirdParty.json @@ -117,7 +117,7 @@ }, { "name": "lucide-slint", - "description": "Implementation of the lucide icon library for Slint.", + "description": "Implementation of the Lucide icon library for Slint.", "icon": "/framework-logos/slint.svg", "shields": [ { @@ -190,5 +190,43 @@ ], "source": "https://github.com/Polydile/dile-components", "documentation": "https://github.com/Polydile/dile-components/blob/master/packages/iconlib/README.md" + }, + { + "name": "strapi-lucide-icons", + "description": "Lucide icon picker custom field for Strapi.", + "icon": "/framework-logos/strapi.svg", + "shields": [ + { + "alt": "Latest Stable Version", + "src": "https://img.shields.io/npm/v/strapi-lucide-icons", + "href": "https://www.npmjs.com/package/strapi-lucide-icons" + }, + { + "alt": "Total Downloads", + "src": "https://img.shields.io/npm/dw/strapi-lucide-icons", + "href": "https://www.npmjs.com/package/strapi-lucide-icons" + } + ], + "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" } ] diff --git a/docs/.vitepress/getHeadConfig.ts b/docs/.vitepress/getHeadConfig.ts new file mode 100644 index 000000000..95ebe50f0 --- /dev/null +++ b/docs/.vitepress/getHeadConfig.ts @@ -0,0 +1,127 @@ +import { HeadConfig } from 'vitepress'; + +const getHeadConfig = ({ + title, + description, + socialTitle, +}: { + title: string; + description: string; + socialTitle?: string; +}): HeadConfig[] => [ + [ + 'link', + { + rel: 'preconnect', + href: 'https://analytics.lucide.dev', + }, + ], + [ + 'script', + { + src: 'https://analytics.lucide.dev/js/script.js', + 'data-domain': 'lucide.dev', + defer: '', + }, + ], + [ + 'meta', + { + property: 'og:locale', + content: 'en_US', + }, + ], + [ + 'meta', + { + property: 'og:type', + content: 'website', + }, + ], + [ + 'meta', + { + property: 'og:site_name', + content: title, + }, + ], + [ + 'meta', + { + property: 'og:title', + content: socialTitle, + }, + ], + [ + 'meta', + { + property: 'og:description', + content: description, + }, + ], + [ + 'meta', + { + property: 'og:url', + content: 'https://lucide.dev', + }, + ], + [ + 'meta', + { + property: 'og:image', + content: 'https://lucide.dev/og.png', + }, + ], + [ + 'meta', + { + property: 'og:image:width', + content: '1200', + }, + ], + [ + 'meta', + { + property: 'og:image:height', + content: '630', + }, + ], + [ + 'meta', + { + property: 'og:image:type', + content: 'image/png', + }, + ], + [ + 'meta', + { + property: 'twitter:card', + content: 'summary_large_image', + }, + ], + [ + 'meta', + { + property: 'twitter:title', + content: socialTitle, + }, + ], + [ + 'meta', + { + property: 'twitter:description', + content: description, + }, + ], + [ + 'meta', + { + property: 'twitter:image', + content: 'https://lucide.dev/og.png', + }, + ], +]; + +export default getHeadConfig; diff --git a/docs/.vitepress/lib/codeExamples/createCodeExamples.ts b/docs/.vitepress/lib/codeExamples/createCodeExamples.ts index e3208c51f..abfa8de53 100644 --- a/docs/.vitepress/lib/codeExamples/createCodeExamples.ts +++ b/docs/.vitepress/lib/codeExamples/createCodeExamples.ts @@ -43,7 +43,7 @@ export default App; language: 'vue', title: 'Vue', code: `