diff --git a/.eslintignore b/.eslintignore index 58e72427e..925b234bd 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,3 +4,8 @@ coverage lib tests node_modules +.eslintrc.js +docs/images +docs/guide/basics/examples +docs/guide/advanced/examples +packages/lucide-react/dynamicIconImports.js diff --git a/.eslintrc.js b/.eslintrc.js index f572e87d3..049c95d76 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,10 +1,13 @@ +const DEFAULT_ATTRS = require('./scripts/render/default-attrs.json'); + module.exports = { + root: true, env: { browser: true, node: true, }, extends: ['airbnb-base', 'prettier'], - plugins: ['import', 'prettier'], + plugins: ['import', 'prettier', '@html-eslint'], rules: { 'no-console': 'off', 'no-param-reassign': 'off', @@ -15,6 +18,7 @@ module.exports = { { singleQuote: true, trailingComma: 'all', + printWidth: 100 }, ], 'import/no-extraneous-dependencies': [ @@ -33,8 +37,43 @@ module.exports = { }, parserOptions: { tsconfigRootDir: __dirname, - project: ['./site/tsconfig.json', './packages/*/tsconfig.json'], + project: ['./docs/tsconfig.json', './packages/*/tsconfig.json'], ecmaVersion: 'latest', sourceType: 'module', }, + overrides: [ + { + files: ['./icons/*.svg'], + parser: '@html-eslint/parser', + rules: { + 'prettier/prettier': 'off', + '@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', + } + }, + ], }; diff --git a/.github/ISSUE_TEMPLATE/01_icon_request.yml b/.github/ISSUE_TEMPLATE/01_icon_request.yml new file mode 100644 index 000000000..a3c4bfca1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01_icon_request.yml @@ -0,0 +1,49 @@ +name: Icon request +description: Suggest a new icon for this project +labels: ['🙌 icon request'] +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 �. + + **Important note**: No new brand logos are allowed, see https://github.com/lucide-icons/lucide/issues/670. + Existing brand icons will also be phased out. For brand icons, consider using https://simpleicons.org, which offers purpose-built SVGs that are also on a 24×24px grid. + - 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 issue before and there weren't any. (Please make sure to also search closed issues, as this issue might already have been resolved.) + 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. + required: true + - label: I am not requesting an icon that includes religious, political imagery or hate symbols. + required: true + - label: I have provided appropriate use cases for the icon(s) requested. + required: true diff --git a/.github/ISSUE_TEMPLATE/02_bug_report.yml b/.github/ISSUE_TEMPLATE/02_bug_report.yml new file mode 100644 index 000000000..4d830cd55 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02_bug_report.yml @@ -0,0 +1,90 @@ +name: Bug report +description: Create a report to help us improve +labels: ['� bug'] +body: + - type: markdown + attributes: + value: | + Before reporting an issue, please search to see if someone has filed a similar issue before. If there is already an open issue, please add a � and/or leave a comment with additional information. + - type: checkboxes + id: packages + attributes: + label: Package + 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-react-native + - label: lucide-solid + - label: lucide-svelte + - label: lucide-vue + - label: lucide-vue-next + - label: Figma plugin + - label: source/main + - label: other/not relevant + validations: + required: true + - type: input + id: version + attributes: + label: Version + description: What version of Lucide are you running? + placeholder: e.g. 0.289.1 + validations: + required: true + - type: checkboxes + id: browsers + attributes: + label: Browser + description: In which browser(s) are you experiencing the issue? You may select more than one. + options: + - label: Chrome/Chromium + - label: Firefox + - label: Safari + - label: Edge + - label: iOS Safari + - label: Opera + - label: Other/not relevant + validations: + required: false + - type: checkboxes + id: operating-systems + attributes: + label: Operating system + description: In which operating systems a you experiencing the issue? You may select more than one. + options: + - label: Windows + - label: Linux + - label: macOS + - label: Other/not relevant + - type: textarea + id: description + attributes: + label: Description + description: Try to describe in detail the problem you're running into and provide additional context about your working environment if necessary. + placeholder: e.g. When I do X, Y happens instead of Z + validations: + required: true + - type: textarea + id: steps-to-reproduce + attributes: + label: Steps to reproduce + description: Please provide a detailed guide on how this issue can be reproduced or a live example with a working reproduction on Codesandbox, JSFiddle or similar. + placeholder: | + 1. Import `check` icon + 2. Add to a React component/view + 3. Run the react app + 4. Notice that the `check` isn't rendering correctly which seems a encoding problem + validations: + required: true + - type: checkboxes + id: checklist + attributes: + label: Checklist + description: Please review the following checklist before submitting your issue. + options: + - label: I have searched if someone has submitted a similar issue before and there weren't any. (Please make sure to also search closed issues, as this issue might already have been resolved.) + required: true diff --git a/.github/ISSUE_TEMPLATE/03_bug_report_site.yml b/.github/ISSUE_TEMPLATE/03_bug_report_site.yml new file mode 100644 index 000000000..36438cf43 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/03_bug_report_site.yml @@ -0,0 +1,62 @@ +name: lucide.dev bug report +description: Help us improve the Lucide site +labels: ['� bug', '� site'] +body: + - type: markdown + attributes: + value: | + Before reporting an issue, please search to see if someone has filed a similar issue before. If there is already an open issue, please add a � and/or leave a comment with additional information. + - type: checkboxes + id: browsers + attributes: + label: Browser + description: In which browser(s) are you experiencing the issue? You may select more than one. + options: + - label: Chrome/Chromium + - label: Firefox + - label: Safari + - label: Edge + - label: iOS Safari + - label: Opera + - label: Other/not relevant + validations: + required: false + - type: checkboxes + id: operating-systems + attributes: + label: Operating system + description: In which operating systems are you experiencing the issue? You may select more than one. + options: + - label: Windows + - label: Linux + - label: macOS + - label: Other/not relevant + - type: textarea + id: description + attributes: + label: Description + description: Try to describe in detail the problem you're running into and provide additional context about your working environment if necessary. + placeholder: e.g. When I do X, Y happens instead of Z + validations: + required: true + - type: textarea + id: stepsToReproduce + attributes: + label: Steps to reproduce + description: Please provide a detailed guide on how this issue can be reproduced. + placeholder: | + 1. I click on an icon + 2. I click on `Copy SVG` in the drawer + 3. I paste from the clipboard + 4. A base64 encoded data URI is inserted. + ... + validations: + required: true + - type: checkboxes + id: checklist + attributes: + label: Checklist + description: Please review the following checklist before submitting your issue. + options: + - label: I have searched if someone has submitted a similar issue before and there weren't any. (Please make sure to also search closed issues, as this issue might already have been resolved.) + required: true diff --git a/.github/ISSUE_TEMPLATE/04_feature_request.yml b/.github/ISSUE_TEMPLATE/04_feature_request.yml new file mode 100644 index 000000000..9b07f3645 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/04_feature_request.yml @@ -0,0 +1,53 @@ +name: Feature request +description: Share with us your ideas on how Lucide could be improved upon. +labels: ['💡 idea'] +body: + - type: markdown + attributes: + value: | + Before submitting a new feature request, please search to see if someone has filed a similar request before. If there is already an open issue, please add a � and/or leave a comment with additional information. + - type: checkboxes + id: packages + attributes: + label: Package + 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-react-native + - label: lucide-solid + - label: lucide-svelte + - label: lucide-vue + - label: lucide-vue-next + - label: Figma plugin + - label: all JS packages + - label: site + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: Try to describe in detail the feature you wish existed. + placeholder: e.g. I want to be able to set extra CSS classes on icon components. + validations: + required: true + - type: textarea + id: use-cases + attributes: + label: Use cases + description: Why do you need this feature? Provide real-life use cases as to why this feature will be useful for others. + placeholder: e.g. I could use the extra classes to add animation or global custom styling to some icons. + validations: + required: true + - type: checkboxes + id: checklist + attributes: + label: Checklist + description: Please check the following items before submitting your issue. + options: + - label: I have searched the existing issues to make sure this bug has not already been reported. + required: true diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index df19ca741..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -labels: "� bug" ---- - - - -## Prerequisites - -* Version: -* Are you running from source/main: -* Are you using a released build: -* Operating system: -* Bits: - -## Step to reproduce - -*(Type here)* - -### Actual behavior - -## Any message or error - -*(Type here)* - -## Resources - -* Links -* Screenshots - - -Here is what a great bug report would look like: - -``` -## Prerequisites - -Version: Release v3.1.0 -Running from: Import using webpack -Operating system: Mac OSX -Bits: 64 bits - -## Step to reproduce - - - Import `check` icon - - Add to a React component/view - - Run the react app - - Notice that the `check` isn't rendering correctly which seems a encoding problem - -### Actual behavior: - - - Import `check` icon - - Add to a React component/view - - Run the react app - - Check is displayed with correct encoding (e.g UTF-8) - -## Any message or error - -No console output -... - -## Resources - -No resources -... -``` diff --git a/.github/ISSUE_TEMPLATE/icon_request.md b/.github/ISSUE_TEMPLATE/icon_request.md deleted file mode 100644 index 33fbd18c8..000000000 --- a/.github/ISSUE_TEMPLATE/icon_request.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: Icon request -about: Suggest an new icon for this project -labels: "🙌 icon request" ---- - - - -## Icon Request - -* Icon name: -* Use case: -* _Screenshots_ of similar icons: diff --git a/.github/PULL_REQUEST_TEMPLATE/new-icon.md b/.github/PULL_REQUEST_TEMPLATE/new-icon.md deleted file mode 100644 index 0d48e1ab6..000000000 --- a/.github/PULL_REQUEST_TEMPLATE/new-icon.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: New icon -about: Add a new icon to the library -labels: "🎨 - -- **Name of the icon** : -- **Tags (alternative names for this icon)** (add them in as a separate json file using the same icon name) : -- **What is the purpose of this icon?** : -- **100% scale preview** : -- **Have you considered alternative possibilities** for its naming or design? : diff --git a/.github/actions/build-and-test.yml b/.github/actions/build-and-test.yml index 59cceca58..47c3293eb 100644 --- a/.github/actions/build-and-test.yml +++ b/.github/actions/build-and-test.yml @@ -9,12 +9,12 @@ inputs: runs: using: "composite" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 16 - - uses: pnpm/action-setup@v2.0.1 + - uses: pnpm/action-setup@v2 name: Install pnpm id: pnpm-install with: diff --git a/.github/actions/check-icons.yml b/.github/actions/check-icons.yml index cdf436552..369918355 100644 --- a/.github/actions/check-icons.yml +++ b/.github/actions/check-icons.yml @@ -9,12 +9,12 @@ inputs: runs: using: "composite" steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 16 - - uses: pnpm/action-setup@v2.0.1 + - uses: pnpm/action-setup@v2 name: Install pnpm id: pnpm-install with: diff --git a/.github/labeler.yml b/.github/labeler.yml index 1bb40fe84..696122eff 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,62 +1,92 @@ # For changed dependencies 📦 dependencies: -- pnpm-lock.yaml +- changed-files: + - any-glob-to-any-file: + - pnpm-lock.yaml # For changes in documentation 📖 documentation: -- docs/*.md -- docs/**/*.md +- changed-files: + - any-glob-to-any-file: + - docs/*.md + - docs/**/*.md # For changes in the site, but not markdown files � site: -- 'docs/**' +- changed-files: + - any-glob-to-any-file: + - 'docs/**' # For changes in the metadata 🫧 metadata: -- 'icons/*.json' -- categories/* +- changed-files: + - any-glob-to-any-file: + - 'icons/*.json' + - categories/* # For changes or added icons 🎨 icon: -- 'icons/*.svg' +- changed-files: + - any-glob-to-any-file: + - 'icons/*.svg' # For changes in the lucide package 🧳 lucide package: -- 'packages/lucide/*' +- changed-files: + - any-glob-to-any-file: + - 'packages/lucide/*' # For changes in the lucide React package ⚛� react package: -- 'packages/lucide-react/*' +- changed-files: + - any-glob-to-any-file: + - 'packages/lucide-react/*' # For changes in the lucide React Native package ⚛� react native package: -- 'packages/lucide-react-native/*' +- changed-files: + - any-glob-to-any-file: + - 'packages/lucide-react-native/*' # For changes in the lucide vue packages 💎 vue package: -- 'packages/lucide-vue/*' -- 'packages/lucide-vue-next/*' +- changed-files: + - any-glob-to-any-file: + - 'packages/lucide-vue/*' + - 'packages/lucide-vue-next/*' # For changes in the lucide angular package 🅰� angular package: -- 'packages/lucide-angular/*' +- changed-files: + - any-glob-to-any-file: + - 'packages/lucide-angular/*' # For changes in the lucide preact package ⚛� preact package: -- 'packages/lucide-preact/*' +- changed-files: + - any-glob-to-any-file: + - 'packages/lucide-preact/*' # For changes in the lucide svelte package 🧣 svelte package: -- 'packages/lucide-svelte/*' +- changed-files: + - any-glob-to-any-file: + - 'packages/lucide-svelte/*' # For changes in the lucide solid package � solid package: -- 'packages/lucide-solid/*' +- changed-files: + - any-glob-to-any-file: + - 'packages/lucide-solid/*' # For changes in the lucide static package 🪨 static package: -- 'packages/lucide-static/*' +- changed-files: + - any-glob-to-any-file: + - 'packages/lucide-static/*' # For changes in the lucide flutter package � flutter package: -- 'packages/lucide-flutter/*' +- changed-files: + - any-glob-to-any-file: + - 'packages/lucide-flutter/*' diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..1bda80942 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,55 @@ + + + + +## What is the purpose of this pull request? + +- [ ] New Icon +- [ ] Bug fix +- [ ] New Feature +- [ ] Documentation update +- [ ] Other: + +### Description + + +### Icon use case + + +### Alternative icon designs + + +## Icon Design Checklist + +### Concept + +- [ ] I have provided valid use cases for each icon. +- [ ] I have not added any a brand or logo icon. +- [ ] I have not used any hate symbols. +- [ ] I have not included any religious or political imagery. + +### Author, credits & license + +- [ ] The icons are solely my own creation. +- [ ] The icons were originally created in # by @ +- [ ] I've based them on the following Lucide icons: +- [ ] I've based them on the following design: + +### Naming + +- [ ] I've read and followed the [naming conventions](https://lucide.dev/guide/design/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 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. +- [ ] I've correctly optimized all icons to two points of precision. + +## Before Submitting + +- [ ] I've read the [Contribution Guidelines](https://github.com/lucide-icons/lucide/blob/main/CONTRIBUTING.md). +- [ ] I've checked if there was an existing PR that solves the same issue. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a84402c2..7b6f6fed5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,33 +15,17 @@ jobs: VERSION: ${{ steps.new-version.outputs.NEW_VERSION }} steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile - name: Fetch tags run: git fetch --all --tags @@ -50,9 +34,6 @@ jobs: id: latest-tag run: echo "LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_OUTPUT - - name: Install dependencies - run: pnpm install - - name: Check if we can patch run: .github/workflows/version-up.sh --minor @@ -66,7 +47,10 @@ jobs: CHANGE_LOG=$(pnpm run generate:changelog --old-tag=${{ steps.latest-tag.outputs.LATEST_TAG }}) CHANGE_LOG=$(tail -n +5 <<< $CHANGE_LOG) echo $CHANGE_LOG - echo "CHANGE_LOG=$CHANGE_LOG" >> $GITHUB_OUTPUT + EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) + echo "CHANGE_LOG<<$EOF" >> $GITHUB_OUTPUT + echo "$CHANGE_LOG" >> $GITHUB_OUTPUT + echo "$EOF" >> $GITHUB_OUTPUT env: GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }} @@ -86,5 +70,6 @@ jobs: if: github.repository == 'lucide-icons/lucide' needs: create-release uses: './.github/workflows/release.yml' + secrets: inherit with: version: ${{ needs.create-release.outputs.VERSION }} diff --git a/.github/workflows/close-stale-prs.yml b/.github/workflows/close-stale-prs.yml index b51e9d6f5..5992c94ac 100644 --- a/.github/workflows/close-stale-prs.yml +++ b/.github/workflows/close-stale-prs.yml @@ -7,7 +7,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v3 + - uses: actions/stale@v9 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. diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 07b21837d..2a340279e 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -9,4 +9,4 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v4 + - uses: actions/labeler@v5 diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 000000000..cdc76ea5a --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,25 @@ +name: Linting + +on: + pull_request: + branches: + - '**' + +jobs: + linting: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 + with: + version: 8 + - uses: actions/setup-node@v4 + with: + node-version: 18 + cache: 'pnpm' + + - name: Install Dependencies + run: pnpm install --frozen-lockfile + + - name: Run Linter + run: pnpm lint diff --git a/.github/workflows/lucide-angular.yml b/.github/workflows/lucide-angular.yml index 51d373d35..d31c0859b 100644 --- a/.github/workflows/lucide-angular.yml +++ b/.github/workflows/lucide-angular.yml @@ -4,39 +4,24 @@ on: pull_request: paths: - packages/lucide-angular/** + - tools/build-icons/** - pnpm-lock.yaml jobs: lucide-angular: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile - name: Build run: pnpm --filter lucide-angular build diff --git a/.github/workflows/lucide-font.yml b/.github/workflows/lucide-font.yml index dfb35a4cc..c12be90b6 100644 --- a/.github/workflows/lucide-font.yml +++ b/.github/workflows/lucide-font.yml @@ -4,52 +4,33 @@ on: pull_request: paths: - icons/** + - tools/build-font/** - pnpm-lock.yaml jobs: lucide-font: runs-on: ubuntu-latest - container: ericfennis/lucide-font:latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3.4.1 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies - run: pnpm install --filter outline-svg + run: pnpm install --frozen-lockfile - name: Outline svg Icons run: pnpm build:outline-icons - - name: Create directory - run: mkdir lucide-font - - - name: Build font - run: fontcustom compile "./outlined" -h -n "lucide" -o ./lucide-font -F + - name: Create font in ./lucide-font + run: pnpm build:font - name: "Upload to Artifacts" - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: lucide-font path: lucide-font diff --git a/.github/workflows/lucide-preact.yml b/.github/workflows/lucide-preact.yml index 9d76816c0..0c071d1b6 100644 --- a/.github/workflows/lucide-preact.yml +++ b/.github/workflows/lucide-preact.yml @@ -4,39 +4,25 @@ on: pull_request: paths: - packages/lucide-preact/** + - tools/build-icons/** + - tools/rollup-plugins/** - pnpm-lock.yaml jobs: lucide-preact: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile - name: Build run: pnpm --filter lucide-preact build diff --git a/.github/workflows/lucide-react-native.yml b/.github/workflows/lucide-react-native.yml index 4674f20e9..f8be337e1 100644 --- a/.github/workflows/lucide-react-native.yml +++ b/.github/workflows/lucide-react-native.yml @@ -4,39 +4,25 @@ on: pull_request: paths: - packages/lucide-react-native/** + - tools/build-icons/** + - tools/rollup-plugins/** - pnpm-lock.yaml jobs: lucide-react-native: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile - name: Build run: pnpm --filter lucide-react-native build diff --git a/.github/workflows/lucide-react.yml b/.github/workflows/lucide-react.yml index 73962b2ef..ff11e530a 100644 --- a/.github/workflows/lucide-react.yml +++ b/.github/workflows/lucide-react.yml @@ -4,39 +4,26 @@ on: pull_request: paths: - packages/lucide-react/** + - tools/build-icons/** + - tools/rollup-plugins/** + - scripts/generateNextJSAliases.mjs - pnpm-lock.yaml jobs: lucide-react: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile - name: Build run: pnpm --filter lucide-react build diff --git a/.github/workflows/lucide-solid.yml b/.github/workflows/lucide-solid.yml index cc351d708..d32f98a1b 100644 --- a/.github/workflows/lucide-solid.yml +++ b/.github/workflows/lucide-solid.yml @@ -4,39 +4,25 @@ on: pull_request: paths: - packages/lucide-solid/** + - tools/build-icons/** + - tools/rollup-plugins/** - pnpm-lock.yaml jobs: lucide-solid: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile - name: Build run: pnpm --filter lucide-solid build diff --git a/.github/workflows/lucide-static.yml b/.github/workflows/lucide-static.yml index 9211a522f..fc0fc4b99 100644 --- a/.github/workflows/lucide-static.yml +++ b/.github/workflows/lucide-static.yml @@ -4,39 +4,24 @@ on: pull_request: paths: - packages/lucide-static/** + - tools/build-icons/** - pnpm-lock.yaml jobs: lucide-static: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile - name: Build run: pnpm --filter lucide-static build diff --git a/.github/workflows/lucide-svelte.yml b/.github/workflows/lucide-svelte.yml index d8c42fa5e..07088c6fb 100644 --- a/.github/workflows/lucide-svelte.yml +++ b/.github/workflows/lucide-svelte.yml @@ -4,39 +4,25 @@ on: pull_request: paths: - packages/lucide-svelte/** + - tools/build-icons/** + - tools/rollup-plugins/** - pnpm-lock.yaml jobs: lucide-svelte: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile - name: Build run: pnpm --filter lucide-svelte build diff --git a/.github/workflows/lucide-vue-next.yml b/.github/workflows/lucide-vue-next.yml index 352042cea..68fd57a84 100644 --- a/.github/workflows/lucide-vue-next.yml +++ b/.github/workflows/lucide-vue-next.yml @@ -4,39 +4,25 @@ on: pull_request: paths: - packages/lucide-vue-next/** + - tools/build-icons/** + - tools/rollup-plugins/** - pnpm-lock.yaml jobs: lucide-vue-next: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile - name: Build run: pnpm --filter lucide-vue-next build diff --git a/.github/workflows/lucide-vue.yml b/.github/workflows/lucide-vue.yml index 78678756d..94f2052ee 100644 --- a/.github/workflows/lucide-vue.yml +++ b/.github/workflows/lucide-vue.yml @@ -4,39 +4,25 @@ on: pull_request: paths: - packages/lucide-vue/** + - tools/build-icons/** + - tools/rollup-plugins/** - pnpm-lock.yaml jobs: lucide-vue: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile - name: Build run: pnpm --filter lucide-vue build diff --git a/.github/workflows/lucide.yml b/.github/workflows/lucide.yml index 315e43bbc..369d0a9e1 100644 --- a/.github/workflows/lucide.yml +++ b/.github/workflows/lucide.yml @@ -4,39 +4,25 @@ on: pull_request: paths: - packages/lucide/** + - tools/build-icons/** + - tools/rollup-plugins/** - pnpm-lock.yaml jobs: lucide: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile - name: Build run: pnpm --filter lucide build diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index e61b4dbc7..19924b755 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -4,26 +4,95 @@ on: pull_request_target: paths: - 'icons/*.svg' - -permissions: - pull-requests: write - contents: write + branches: + - main jobs: - add-changed-icons-comment: + lint-contributors: + if: github.repository == 'lucide-icons/lucide' runs-on: ubuntu-latest + permissions: + contents: read steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 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@v35 + uses: tj-actions/changed-files@v41 + with: + files: icons/* + - uses: actions/setup-node@v4 + - name: Install simple-git (safer and faster than installing all deps) + run: npm install simple-git + - name: Generate annotations + run: node ./scripts/updateContributors.mjs + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + FETCH_DEPTH: ${{ github.event.pull_request.commits }} + CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} + - name: Generate annotations + env: + ANNOTATION_SEVERITY: notice + ANNOTATION_TITLE: Contributors have changed! + ANNOTATION_DESCRIPTION: Don't add people who have only performed automatic optimizations. + run: | + git diff --unified=0 -- icons/*.json | # diff icon metadata (unified=0 gives the correct chunk line number) + perl -ne '/^(\+|- |@)/ && print' | # get chunks (lines that start with "+++", "@@", "+ ", "- ") + perl -pe 's/\n/%0A/' | # url encode line breaks (\n -> %0A) + perl -pe 's/%0A(\+\+\+ b\/)/\n\1/g' | # split chunks(one chunk per line) + perl -pe "s/\+\+\+ b\/([^@]*)%0A@@ -(\d+)[^\s]* \+(\d+)[^@]*@@(.*)/::$ANNOTATION_SEVERITY file=\1,line=\2,endLine=\3,title=$ANNOTATION_TITLE::$ANNOTATION_DESCRIPTION%0A\4/" + # Example for the previous substitution + # input: +++ b/icons/accessibility.json%0A@@ -2,0 +3 @@%0A+ "contributors": ["hi"],%0A@@ -13 +14 @@%0A+}%0A + # output: ::$ANNOTATION_SEVERITY file=icons/accessibility.json,line=2,endLine=3,title=$ANNOTATION_TITLE::$ANNOTATION_DESCRIPTION%0A%0A+ "contributors": ["hi"],%0A@@ -13 +14 @@%0A+}%0A + # - name: Fail if contributors have changed + # run: git diff --exit-code -- icons/*.json + + generate-changed-icons-comment-data: + if: github.repository == 'lucide-icons/lucide' + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + generate-1px-stroke-width: ${{ steps.generate-1px-stroke-width.outputs.body }} + generate-2px-stroke-width: ${{ steps.generate-2px-stroke-width.outputs.body }} + generate-3px-stroke-width: ${{ steps.generate-3px-stroke-width.outputs.body }} + generate-24px-dpi-preview: ${{ steps.generate-24px-dpi-preview.outputs.body }} + generate-cohesion-check-random: ${{ steps.generate-cohesion-check-random.outputs.body }} + generate-cohesion-check-squares: ${{ steps.generate-cohesion-check-squares.outputs.body }} + generate-x-rays: ${{ steps.generate-x-rays.outputs.body }} + + steps: + - uses: actions/checkout@v4 + 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 with: files: icons/*.svg + - name: Generate 24px dpi preview + id: generate-24px-dpi-preview + env: + CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} + run: | + delimiter="$(openssl rand -hex 8)" + echo "body<<$delimiter" >> $GITHUB_OUTPUT + while IFS= read -r file; do + cat "$file" | # get file content + tr '\n' ' ' | # remove line breaks + sed -e 's/]*>//g' | # remove attributes from svg element + base64 -w 0 | # encode svg + sed "s|.*|\"$file\" |" + done <<< "$CHANGED_FILES" | tr '\n' ' ' >> $GITHUB_OUTPUT + echo >> $GITHUB_OUTPUT + echo "$delimiter" >> $GITHUB_OUTPUT - name: Generate cohesion check random id: generate-cohesion-check-random + env: + CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} run: | delimiter="$(openssl rand -hex 8)" echo "body<<$delimiter" >> $GITHUB_OUTPUT @@ -33,11 +102,13 @@ jobs: sed -e 's/]*>//g' | # remove attributes from svg element base64 -w 0 | # encode svg sed "s|.*|\"$file\" |" - done | tr '\n' ' ' >> $GITHUB_OUTPUT + done <<< "$CHANGED_FILES" | tr '\n' ' ' >> $GITHUB_OUTPUT echo >> $GITHUB_OUTPUT echo "$delimiter" >> $GITHUB_OUTPUT - name: Generate cohesion check squares id: generate-cohesion-check-squares + env: + CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} run: | delimiter="$(openssl rand -hex 8)" echo "body<<$delimiter" >> $GITHUB_OUTPUT @@ -47,35 +118,39 @@ jobs: sed -e 's/]*>//g' | # remove attributes from svg element base64 -w 0 | # encode svg sed "s|.*|\"$file\" |" - done | tr '\n' ' ' >> $GITHUB_OUTPUT + done <<< "$CHANGED_FILES" | tr '\n' ' ' >> $GITHUB_OUTPUT echo >> $GITHUB_OUTPUT echo "$delimiter" >> $GITHUB_OUTPUT - name: Generate 1px stroke-width id: generate-1px-stroke-width + env: + CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} run: | delimiter="$(openssl rand -hex 8)" echo "body<<$delimiter" >> $GITHUB_OUTPUT - for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + while IFS= read -r file; do cat "$file" | # get file content tr '\n' ' ' | # remove line breaks sed -e 's/]*>//g' | # remove attributes from svg element base64 -w 0 | # encode svg sed "s|.*|\"$file\" |" - done | tr '\n' ' ' >> $GITHUB_OUTPUT + done <<< "$CHANGED_FILES" | tr '\n' ' ' >> $GITHUB_OUTPUT echo >> $GITHUB_OUTPUT echo "$delimiter" >> $GITHUB_OUTPUT - name: Generate 2px stroke-width id: generate-2px-stroke-width + env: + CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} run: | delimiter="$(openssl rand -hex 8)" echo "body<<$delimiter" >> $GITHUB_OUTPUT - for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + while IFS= read -r file; do cat "$file" | # get file content tr '\n' ' ' | # remove line breaks sed -e 's/]*>//g' | # remove attributes from svg element base64 -w 0 | # encode svg sed "s|.*|\"$file\" |" - done | tr '\n' ' ' >> $GITHUB_OUTPUT + done <<< "$CHANGED_FILES" | tr '\n' ' ' >> $GITHUB_OUTPUT echo >> $GITHUB_OUTPUT echo "$delimiter" >> $GITHUB_OUTPUT - name: Generate 3px stroke-width @@ -83,29 +158,39 @@ jobs: run: | delimiter="$(openssl rand -hex 8)" echo "body<<$delimiter" >> $GITHUB_OUTPUT - for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + while IFS= read -r file; do cat "$file" | # get file content tr '\n' ' ' | # remove line breaks sed -e 's/]*>//g' | # remove attributes from svg element base64 -w 0 | # encode svg sed "s|.*|\"$file\" |" - done | tr '\n' ' ' >> $GITHUB_OUTPUT + done <<< "$CHANGED_FILES" | tr '\n' ' ' >> $GITHUB_OUTPUT echo >> $GITHUB_OUTPUT echo "$delimiter" >> $GITHUB_OUTPUT - name: Generate X-rays id: generate-x-rays + env: + CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} run: | delimiter="$(openssl rand -hex 8)" echo "body<<$delimiter" >> $GITHUB_OUTPUT - for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + while IFS= read -r file; do cat "$file" | # get file content tr '\n' ' ' | # remove line breaks sed -e 's/]*>//g' | # remove attributes from svg element base64 -w 0 | # encode svg sed "s|.*|\"$file\" |" - done | tr '\n' ' ' >> $GITHUB_OUTPUT + done <<< "$CHANGED_FILES" | tr '\n' ' ' >> $GITHUB_OUTPUT echo >> $GITHUB_OUTPUT echo "$delimiter" >> $GITHUB_OUTPUT + + add-changed-icons-comment: + if: github.repository == 'lucide-icons/lucide' + runs-on: ubuntu-latest + permissions: + pull-requests: write + needs: [generate-changed-icons-comment-data] + steps: - name: Find Comment uses: peter-evans/find-comment@v2 id: fc @@ -114,27 +199,31 @@ jobs: comment-author: 'github-actions[bot]' body-includes: Added or changed icons - name: Create or update comment - uses: peter-evans/create-or-update-comment@v2 + uses: peter-evans/create-or-update-comment@v3 with: comment-id: ${{ steps.fc.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} body: | ### Added or changed icons - ${{ steps.generate-2px-stroke-width.outputs.body }}
+ ${{ needs.generate-changed-icons-comment-data.outputs.generate-2px-stroke-width }}
Preview cohesion - ${{ steps.generate-cohesion-check-squares.outputs.body }}
- ${{ steps.generate-2px-stroke-width.outputs.body }}
- ${{ steps.generate-cohesion-check-random.outputs.body }}
+ ${{ needs.generate-changed-icons-comment-data.outputs.generate-cohesion-check-squares }}
+ ${{ needs.generate-changed-icons-comment-data.outputs.generate-2px-stroke-width }}
+ ${{ needs.generate-changed-icons-comment-data.outputs.generate-cohesion-check-random }}
Preview stroke widths - ${{ steps.generate-1px-stroke-width.outputs.body }}
- ${{ steps.generate-2px-stroke-width.outputs.body }}
- ${{ steps.generate-3px-stroke-width.outputs.body }}
+ ${{ needs.generate-changed-icons-comment-data.outputs.generate-1px-stroke-width }}
+ ${{ needs.generate-changed-icons-comment-data.outputs.generate-2px-stroke-width }}
+ ${{ needs.generate-changed-icons-comment-data.outputs.generate-3px-stroke-width }}
+
+
+ DPI Preview (24px) + ${{ needs.generate-changed-icons-comment-data.outputs.generate-24px-dpi-preview }}
Icon X-rays - ${{ steps.generate-x-rays.outputs.body }} + ${{ needs.generate-changed-icons-comment-data.outputs.generate-x-rays }}
edit-mode: replace diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df8a2cde0..f74019305 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,6 +9,7 @@ on: inputs: version: required: true + description: Version type: string workflow_dispatch: @@ -38,6 +39,7 @@ jobs: runs-on: ubuntu-latest needs: pre-release strategy: + fail-fast: false matrix: package: [ 'lucide', @@ -51,36 +53,20 @@ jobs: 'lucide-svelte', ] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 + with: + version: 8 + - uses: actions/setup-node@v4 with: node-version: 18 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install - with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Set Auth Token - run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }} + run: npm config set //registry.npmjs.org/:_authToken ${{ inputs.NPM_TOKEN || secrets.NPM_TOKEN }} - name: Set new version run: pnpm --filter ${{ matrix.package }} version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version @@ -99,32 +85,15 @@ jobs: runs-on: ubuntu-latest needs: [pre-release, lucide-font] steps: - - uses: actions/checkout@v3 - - uses: actions/download-artifact@v2 - - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v3 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile @@ -148,47 +117,27 @@ jobs: if: github.repository == 'lucide-icons/lucide' runs-on: ubuntu-latest needs: pre-release - container: ericfennis/lucide-font:latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3.4.1 + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 with: - node-version: 16 - - - uses: pnpm/action-setup@v2.0.1 - name: Install pnpm - id: pnpm-install + version: 8 + - uses: actions/setup-node@v4 with: - version: 7 - run_install: false - - - name: Get pnpm store directory - id: pnpm-cache - run: | - echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + node-version: 18 + cache: 'pnpm' - name: Install dependencies - run: pnpm install --filter outline-svg + run: pnpm install --frozen-lockfile - name: Outline svg Icons run: pnpm build:outline-icons - - name: Create directory - run: mkdir lucide-font - - - name: Build font - run: fontcustom compile "./outlined" -h -n "lucide" -o ./lucide-font -F + - name: Create font in ./lucide-font + run: pnpm build:font - name: "Upload to Artifacts" - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v3 with: name: lucide-font path: lucide-font @@ -202,8 +151,8 @@ jobs: ] steps: - - uses: actions/checkout@v3 - - uses: actions/download-artifact@v2 + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v3 - name: Zip font and icons run: | zip -r lucide-font-${{ needs.pre-release.outputs.VERSION }}.zip lucide-font diff --git a/.gitignore b/.gitignore index 343f9357c..782035325 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,9 @@ packages/**/src/icons/*.ts packages/**/src/icons/*.tsx packages/**/src/aliases.ts packages/**/src/dynamicIconImports.ts +packages/**/dynamicIconImports.js +packages/**/dynamicIconImports.d.ts +packages/**/dynamicIconImports.js.map packages/**/LICENSE categories.json tags.json diff --git a/.npmignore b/.npmignore index 718d7e342..f2e2f7010 100644 --- a/.npmignore +++ b/.npmignore @@ -4,7 +4,6 @@ stats node_modules tests scripts -site src build babel.config.js diff --git a/.vscode/settings.json b/.vscode/settings.json index 9e7294402..59e014f60 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,11 @@ "devs", "preact", "Preact" - ] + ], + "eslint.enable": true, + "eslint.validate": [ + "javascript", + "svg" + ], + "svg.preview.background": "transparent" } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 76729a914..c85df7158 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,7 +33,7 @@ Here you can find instructions on how to implement the guidelines with different #### [Adobe Illustrator Guide](https://lucide.dev/docs/illustrator-guide) -You can also [download an Adobe Illustrator template](https://lucide.dev/templates/illustrator-template.ai). +You can also [download an Adobe Illustrator template](https://github.com/lucide-icons/lucide/blob/main/docs/public/templates/illustrator_template.ai). #### [Inkscape Guide](https://lucide.dev/docs/inkscape-guide) @@ -144,16 +144,19 @@ Root directories ```sh lucide -| ├── docs +│ ├── guide ├── icons ├── packages -├── scripts -└── site +└── scripts ``` ### Docs +The lucide.dev website is using [vitepress](https://vitepress.dev/) to generate the static website. The markdown files are located in the docs directory. + +### Guides + Detailed documentation about: installation, guides, packages, design guides etc. ### Icons @@ -170,10 +173,6 @@ Includes all the (npm) packages of lucide. Includes usefully scripts to automate certain jobs. Big part of the scripts is the template generation, for example it generates icon components for all the packages. These scripts are usually executed from the "scripts" section in the package.json. -### site - -The lucide.dev website is using [vitepress](https://vitepress.dev/) to generate the static website. The markdown files are located in the docs directory. - ## Documentation The documentation files are located in the [docs](https://github.com/lucide-icons/lucide/tree/main/docs) directory. All these markdown files will be loaded in the build of the lucide.dev website. diff --git a/README.md b/README.md index 47c9e3f16..81235994f 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,14 @@ Community-run fork of [Feather Icons](https://github.com/feathericons/feather), open for anyone to contribute icons. -It began after growing disaffection with the [Feather Icons](https://github.com/feathericons/feather) project moderation. With over 300+ open issues and over 100+ open PRs, the Feather Icons project has been abandoned. This unfortunately means that hundreds of developers and designers wasted their time contributing to Feather Icons with no chance of PRs being accepted. +It began after growing dissatisfaction with the [Feather Icons](https://github.com/feathericons/feather) project moderation. With over 300+ open issues and over 100+ open PRs, the Feather Icons project has been abandoned. This unfortunately means that hundreds of developers and designers wasted their time contributing to Feather Icons with no chance of PRs being accepted. Lucide is trying to expand the icon set as much as possible while staying faithful to the original simplistic design language. We do this as a community of devs and designers and hope that you'll join us! ### Why choose Lucide over Feather Icons - More icons to work with: Lucide already has hundreds of icons more than Feather does. -- Official librairies and integrations with popular frameworks and design tools. +- Official libraries and integrations with popular frameworks and design tools. - Well maintained code base. - Active community, regularly growing and improving the set. @@ -35,7 +35,6 @@ Lucide is trying to expand the icon set as much as possible while staying faithf - [Static (svg sprite, font, icons ..)](#static-svg-sprite-font-icons-) - [Figma](#figma) - [Laravel](#laravel) - - [Flutter](#flutter) - [Svelte](#svelte) - [Solid](#solid) - [Hyva](#hyva) @@ -200,16 +199,6 @@ composer require mallardduck/blade-lucide-icons For more details, see the [documentation](https://github.com/mallardduck/blade-lucide-icons/blob/main/README.md). -### Flutter - -Implementation of Lucide icon library for Flutter applications. - -```sh -flutter pub add lucide_icons -``` - -For more details, see the [pub.dev](https://pub.dev/packages/lucide_icons). - ### Svelte Implementation of the lucide icon library for Svelte applications. @@ -285,6 +274,9 @@ Thank you to all the people who contributed to Lucide! ## Sponsors + - Powered by Vercel + Powered by Vercel + +DigitalOcean Referral Badge diff --git a/categories/files.json b/categories/files.json index 8814fb2ba..4d1fa3ad9 100644 --- a/categories/files.json +++ b/categories/files.json @@ -1,5 +1,5 @@ { "$schema": "../category.schema.json", "title": "File icons", - "icon": "layout" + "icon": "panels-top-left" } \ No newline at end of file diff --git a/categories/layout.json b/categories/layout.json index 3d03c8d1f..034ea4be9 100644 --- a/categories/layout.json +++ b/categories/layout.json @@ -1,5 +1,5 @@ { "$schema": "../category.schema.json", "title": "Layout", - "icon": "layout" + "icon": "panels-top-left" } \ No newline at end of file diff --git a/categories/transportation.json b/categories/transportation.json index f5fd237e8..0515475d3 100644 --- a/categories/transportation.json +++ b/categories/transportation.json @@ -1,5 +1,5 @@ { "$schema": "../category.schema.json", "title": "Transportation", - "icon": "train" + "icon": "train-front" } \ No newline at end of file diff --git a/category.schema.json b/category.schema.json index fa21ca809..7229f210e 100644 --- a/category.schema.json +++ b/category.schema.json @@ -12,8 +12,10 @@ }, "title": "Lucide Icons category schema", "type": "object", + "additionalProperties": false, + "required": ["$schema", "icon", "title"], "properties": { - "title": { + "$schema": { "type": "string" }, "description": { @@ -22,13 +24,12 @@ "icon": { "type": "string" }, + "title": { + "type": "string" + }, "weight": { "type": "integer" } }, - "required": [ - "title", - "icon" - ], "description": "A JSON Schema for categories defined by Lucide Icons." } diff --git a/docs/.vitepress/api/gh-icon/[...data].get.ts b/docs/.vitepress/api/gh-icon/[...data].get.ts index f688c57ab..eacfb498f 100644 --- a/docs/.vitepress/api/gh-icon/[...data].get.ts +++ b/docs/.vitepress/api/gh-icon/[...data].get.ts @@ -1,41 +1,53 @@ -import { eventHandler, setResponseHeader, defaultContentType } from 'h3' -import { renderToString, renderToStaticMarkup } from 'react-dom/server' -import { createElement } from 'react' +import { eventHandler, setResponseHeader, defaultContentType } from 'h3'; +import { renderToString, renderToStaticMarkup } from 'react-dom/server'; +import { createElement } from 'react'; import SvgPreview from '../../lib/SvgPreview/index.tsx'; -import iconNodes from '../../data/iconNodes' -import createLucideIcon from 'lucide-react/src/createLucideIcon' +import iconNodes from '../../data/iconNodes'; +import createLucideIcon from 'lucide-react/src/createLucideIcon'; import Backdrop from '../../lib/SvgPreview/Backdrop.tsx'; export default eventHandler((event) => { - const { params } = event.context + const { params } = event.context; - const [name, svgData] = params.data.split('/'); - const data = svgData.slice(0, -4); + const pathData = params.data.split('/'); + const data = pathData.at(-1).slice(0, -4); + const [name] = pathData; const src = Buffer.from(data, 'base64').toString('utf8'); - const children = [] + const children = []; - if (name in iconNodes) { - const iconNode = iconNodes[name] + // Finds the longest matching icon to be use as the backdrop. + // For `square-dashed-bottom-code` it suggests `square-dashed-bottom-code`. + // For `square-dashed-bottom-i-dont-exist` it suggests `square-dashed-bottom`. + const backdropName = name + .split('-') + .map((_, idx, arr) => arr.slice(0, idx + 1).join('-')) + .reverse() + .find((groupName) => groupName in iconNodes); + if (backdropName) { + const iconNode = iconNodes[backdropName]; - const LucideIcon = createLucideIcon(name, iconNode) - const svg = renderToStaticMarkup(createElement(LucideIcon)) + const LucideIcon = createLucideIcon(backdropName, iconNode); + const svg = renderToStaticMarkup(createElement(LucideIcon)); const backdropString = svg.replace(/]*>|<\/svg>/g, ''); - children.push(createElement(Backdrop, { backdropString, src })) + children.push( + createElement(Backdrop, { + backdropString, + src, + color: name in iconNodes ? 'red' : '#777', + }) + ); } const svg = Buffer.from( // We can't use jsx here, is not supported here by nitro. - renderToString(createElement(SvgPreview, {src, showGrid: true}, children)).replace( - />/, - '>' - ) + renderToString(createElement(SvgPreview, { src, showGrid: true }, children)) ).toString('utf8'); - defaultContentType(event, 'image/svg+xml') - setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000') + defaultContentType(event, 'image/svg+xml'); + setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000'); - return svg -}) + return svg; +}); diff --git a/docs/.vitepress/api/gh-icon/dpi/[...data].get.ts b/docs/.vitepress/api/gh-icon/dpi/[...data].get.ts new file mode 100644 index 000000000..a09fcb2af --- /dev/null +++ b/docs/.vitepress/api/gh-icon/dpi/[...data].get.ts @@ -0,0 +1,73 @@ +import { eventHandler, setResponseHeader, defaultContentType } from 'h3'; +import { Resvg, initWasm } from '@resvg/resvg-wasm'; +import wasm from './loadWasm'; + +var initializedResvg = initWasm(wasm); + +export default eventHandler(async (event) => { + const { params = {} } = event.context; + await initializedResvg; + + const imageSize = 96; + const [iconSizeString, svgData] = params.data.split('/'); + const iconSize = parseInt(iconSizeString, 10); + const data = svgData.slice(0, -4); + + const src = Buffer.from(data, 'base64').toString('utf8'); + const svg = (src.includes('${src}`) + .replace(/(\r\n|\n|\r)/gm, '') + .replace( + /]*/, + ` + + + + + + +`; +}); diff --git a/docs/.vitepress/api/gh-icon/dpi/loadWasm.ts b/docs/.vitepress/api/gh-icon/dpi/loadWasm.ts new file mode 100644 index 000000000..2369e8941 --- /dev/null +++ b/docs/.vitepress/api/gh-icon/dpi/loadWasm.ts @@ -0,0 +1,15 @@ +import fs from 'fs'; +import module from 'node:module'; +/* WASM_IMPORT */ + +let wasm; + +if (process.env.NODE_ENV === 'development') { + const require = module.createRequire(import.meta.url); + + wasm = fs.readFileSync(require.resolve('@resvg/resvg-wasm/index_bg.wasm')); +} else { + wasm = resvg_wasm; +} + +export default wasm; diff --git a/docs/.vitepress/api/gh-icon/stroke-width/[...data].get.ts b/docs/.vitepress/api/gh-icon/stroke-width/[...data].get.ts index 5618ec53a..1354b1ae4 100644 --- a/docs/.vitepress/api/gh-icon/stroke-width/[...data].get.ts +++ b/docs/.vitepress/api/gh-icon/stroke-width/[...data].get.ts @@ -22,10 +22,19 @@ export default eventHandler((event) => { const svg = Buffer.from( // We can't use jsx here, is not supported here by nitro. - renderToString(createElement(Icon, { strokeWidth })).replace( - />/, - '>' - ) + renderToString(createElement(Icon, { strokeWidth })) + .replace(/fill\="none"/, 'fill="#fff"') + .replace( + />/, + `>` + ) ).toString('utf8'); defaultContentType(event, 'image/svg+xml') diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index b329dfecd..173ed778b 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,14 +1,6 @@ import { fileURLToPath, URL } from 'node:url' -import path from 'path'; import { defineConfig } from 'vitepress' -import { createWriteStream } from 'node:fs' -import { resolve } from 'node:path' -import { SitemapStream } from 'sitemap' import sidebar from './sidebar'; -import fs from 'fs'; - -const links = [] - const title = "Lucide"; const socialTitle = "Lucide Icons"; @@ -20,6 +12,7 @@ export default defineConfig({ description, cleanUrls: true, outDir: '.vercel/output/static', + srcExclude: ['**/README.md'], vite: { resolve: { alias: [ @@ -40,7 +33,7 @@ export default defineConfig({ }, head: [ [ 'script', { - src: 'https://plausible.io/js/script.js', + src: 'https://analytics.lucide.dev/js/script.js', 'data-domain': 'lucide.dev', defer: '' }], @@ -111,6 +104,7 @@ export default defineConfig({ { text: 'Icons', link: '/icons/' }, { text: 'Guide', link: '/guide/' }, { text: 'Packages', link: '/packages' }, + { text: 'Showcase', link: '/showcase' }, { text: 'License', link: '/license' }, ], sidebar, @@ -125,33 +119,12 @@ export default defineConfig({ editLink: { pattern: 'https://github.com/lucide-icons/lucide/edit/main/docs/:path' }, - }, - transformHtml: (_, id, { pageData }) => { - if (/[\\/]404\.html$/.test(id)) { - return + carbonAds: { + code: 'CWYIC53U', + placement: 'lucidedev' } - - if (pageData.relativePath.startsWith('icons/')) { - links.push({ - url: pageData.relativePath.replace(/((^|\/)index)?\.md$/, '$2'), - lastmod: pageData?.params?.changedRelease?.date - }) - return - } - - links.push({ - url: pageData.relativePath.replace(/((^|\/)index)?\.md$/, '$2'), - lastmod: pageData.lastUpdated - }) - }, - buildEnd: async ({ outDir }) => { - const sitemap = new SitemapStream({ - hostname: 'https://lucide.dev/' - }) - const writeStream = createWriteStream(resolve(outDir, 'sitemap.xml')) - sitemap.pipe(writeStream) - links.forEach((link) => sitemap.write(link)) - sitemap.end() - await new Promise((r) => writeStream.on('finish', r)) }, + sitemap: { + hostname: 'https://lucide.dev/' + } }) diff --git a/docs/.vitepress/data/companiesData.json b/docs/.vitepress/data/companiesData.json new file mode 100644 index 000000000..8e1439ad7 --- /dev/null +++ b/docs/.vitepress/data/companiesData.json @@ -0,0 +1,50 @@ +[ + { + "name": "Vercel", + "url": "https://vercel.com", + "image": { + "light": "/company-logos/vercel-light.svg", + "dark": "/company-logos/vercel-dark.svg" + } + }, + { + "name": "Supabase", + "url": "https://supabase.com", + "image": { + "light": "/company-logos/supabase-light.svg", + "dark": "/company-logos/supabase-dark.svg" + } + }, + { + "name": "Obsidian", + "url": "https://obsidian.md", + "image": { + "light": "/company-logos/obsidian-light.svg", + "dark": "/company-logos/obsidian-dark.svg" + } + }, + { + "name": "Open Collective", + "url": "https://opencollective.com", + "image": { + "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" + } + } +] diff --git a/docs/.vitepress/data/componentLibrariesData.json b/docs/.vitepress/data/componentLibrariesData.json new file mode 100644 index 000000000..c0534b9aa --- /dev/null +++ b/docs/.vitepress/data/componentLibrariesData.json @@ -0,0 +1,18 @@ +[ + { + "name": "Shadcn/ui", + "url": "https://ui.shadcn.com/", + "image": { + "light": "/library-logos/shadcn-ui-light.svg", + "dark": "/library-logos/shadcn-ui-dark.svg" + } + }, + { + "name": "Tamagui", + "url": "https://tamagui.dev/", + "image": { + "light": "/library-logos/tamagui.svg", + "dark": "/library-logos/tamagui.svg" + } + } +] diff --git a/docs/.vitepress/lib/SvgPreview/Backdrop.tsx b/docs/.vitepress/lib/SvgPreview/Backdrop.tsx index dfb1a9ab4..be9362523 100644 --- a/docs/.vitepress/lib/SvgPreview/Backdrop.tsx +++ b/docs/.vitepress/lib/SvgPreview/Backdrop.tsx @@ -1,11 +1,12 @@ import React from 'react'; interface BackdropProps { - src: string - backdropString: string + src: string; + color?: string; + backdropString: string; } -const Backdrop = ({ src, backdropString }: BackdropProps): JSX.Element => { +const Backdrop = ({ src, color = 'red', backdropString }: BackdropProps): JSX.Element => { return ( <> @@ -16,8 +17,8 @@ const Backdrop = ({ src, backdropString }: BackdropProps): JSX.Element => { patternUnits="userSpaceOnUse" patternTransform="rotate(45 50 50)" > - - + + @@ -29,20 +30,8 @@ const Backdrop = ({ src, backdropString }: BackdropProps): JSX.Element => { - - + + { y="0" width="24" height="24" - fill="red" + fill={color} opacity={0.5} stroke="none" mask={'url(#svg-preview-backdrop-mask-fill)'} /> - ) -} - + ); +}; export default Backdrop; diff --git a/docs/.vitepress/lib/SvgPreview/index.tsx b/docs/.vitepress/lib/SvgPreview/index.tsx index 056b9ae5e..f14523e6e 100644 --- a/docs/.vitepress/lib/SvgPreview/index.tsx +++ b/docs/.vitepress/lib/SvgPreview/index.tsx @@ -4,7 +4,7 @@ import { getPaths, assert } from './utils'; const Grid = ({ radius, - fill, + fill = '#fff', ...props }: { strokeWidth: number; @@ -12,6 +12,7 @@ const Grid = ({ } & PathProps<'stroke', 'strokeWidth'>) => ( ) => { return ( - {paths - .filter(({ circle }) => circle) - .map(({ c, prev, next, circle: { x, y, r } }) => - c.name === 'circle' ? ( - - ) : ( - <> - - - - ) - )} + {paths.map( + ({ c, prev, next, circle }, i) => + circle && ( + + {c.name !== "circle" && ( + + )} + + + + ), + )} + + ); +}; + +const Handles = ({ + paths, + ...props +}: { paths: Path[] } & PathProps< + 'strokeWidth' | 'stroke' | 'strokeDasharray' | 'strokeOpacity', + any +>) => { + console.log(paths); + return ( + + {paths.map(({ c, prev, next, cp1, cp2 }) => ( + <> + {cp1 && } + {cp1 && } + {cp2 && } + {cp2 && } + + ))} ); }; @@ -207,7 +252,12 @@ const SvgPreview = React.forwardRef< >(({ src, children, showGrid = false, ...props }, ref) => { const paths = typeof src === 'string' ? getPaths(src) : src; - const darkModeCss = `@media screen and (prefers-color-scheme: dark) { + const darkModeCss = `@media screen and (prefers-color-scheme: light) { + .svg-preview-grid-rect { fill: none } +} +@media screen and (prefers-color-scheme: dark) { + .svg-preview-grid-rect { fill: none } + .svg .svg-preview-grid-group, .svg-preview-radii-group, .svg-preview-shadow-mask-group, @@ -232,6 +282,7 @@ const SvgPreview = React.forwardRef< {showGrid && } + + {children} ); diff --git a/docs/.vitepress/lib/SvgPreview/types.ts b/docs/.vitepress/lib/SvgPreview/types.ts index 15023fd0e..b36df0ca6 100644 --- a/docs/.vitepress/lib/SvgPreview/types.ts +++ b/docs/.vitepress/lib/SvgPreview/types.ts @@ -8,7 +8,9 @@ export type Path = { prev: Point; next: Point; isStart: boolean; - circle: { x: number; y: number; r: number }; + circle?: { x: number; y: number; r: number }; + cp1?: Point; + cp2?: Point; c: ReturnType[number]; }; diff --git a/docs/.vitepress/lib/SvgPreview/utils.ts b/docs/.vitepress/lib/SvgPreview/utils.ts index 7c9d79ecb..2a9974a91 100644 --- a/docs/.vitepress/lib/SvgPreview/utils.ts +++ b/docs/.vitepress/lib/SvgPreview/utils.ts @@ -63,7 +63,7 @@ export const getPaths = (src: string) => { c: typeof commands[number], next: Point, d?: string, - circle?: Path['circle'] + extras?: { circle?: Path['circle']; cp1?: Path['cp1']; cp2?: Path['cp2'] } ) => { assert(prev); paths.push({ @@ -71,7 +71,7 @@ export const getPaths = (src: string) => { d: d || `M ${prev.x} ${prev.y} L ${next.x} ${next.y}`, prev, next, - circle, + ...extras, isStart: start === prev, }); prev = next; @@ -110,7 +110,10 @@ export const getPaths = (src: string) => { } case SVGPathData.CURVE_TO: { assert(prev); - addPath(c, c, `M ${prev.x} ${prev.y} ${encodeSVGPath(c)}`); + addPath(c, c, `M ${prev.x} ${prev.y} ${encodeSVGPath(c)}`, { + cp1: { x: c.x1, y: c.y1 }, + cp2: { x: c.x2, y: c.y2 }, + }); break; } case SVGPathData.SMOOTH_CURVE_TO: { @@ -146,13 +149,20 @@ export const getPaths = (src: string) => { y1: prev.y - reflectedCp1.y, x2: c.x2, y2: c.y2, - })}` + })}`, + { + cp1: { x: prev.x - reflectedCp1.x, y: prev.y - reflectedCp1.y }, + cp2: { x: c.x2, y: c.y2 }, + } ); break; } case SVGPathData.QUAD_TO: { assert(prev); - addPath(c, c, `M ${prev.x} ${prev.y} ${encodeSVGPath(c)}`); + addPath(c, c, `M ${prev.x} ${prev.y} ${encodeSVGPath(c)}`, { + cp1: { x: c.x1, y: c.y1 }, + cp2: { x: c.x1, y: c.y1 }, + }); break; } case SVGPathData.SMOOTH_QUAD_TO: { @@ -197,7 +207,11 @@ export const getPaths = (src: string) => { y: c.y, x1: prevCP.x, y1: prevCP.y, - })}` + })}`, + { + cp1: { x: prevCP.x, y: prevCP.y }, + cp2: { x: prevCP.x, y: prevCP.y }, + } ); break; } @@ -218,7 +232,7 @@ export const getPaths = (src: string) => { c, c, `M ${prev.x} ${prev.y} A${c.rX} ${c.rY} ${c.xRot} ${c.lArcFlag} ${c.sweepFlag} ${c.x} ${c.y}`, - c.rX === c.rY ? { ...center, r: c.rX } : undefined + { circle: c.rX === c.rY ? { ...center, r: c.rX } : undefined } ); break; } diff --git a/docs/.vitepress/lib/createCodeExamples.ts b/docs/.vitepress/lib/createCodeExamples.ts index 2de4a5b23..41c75551c 100644 --- a/docs/.vitepress/lib/createCodeExamples.ts +++ b/docs/.vitepress/lib/createCodeExamples.ts @@ -1,41 +1,29 @@ import { - BUNDLED_LANGUAGES, - type IThemeRegistration -} from 'shiki' + bundledLanguages, + type ThemeRegistration +} from 'shikiji' import { getHighlighter, -} from 'shiki-processor' +} from 'shikiji' + type CodeExampleType = { title: string, - lang: string, - codes: { - language?: string, - code: string, - metastring?: string, - }[], + language: string, + code: string, }[] const getIconCodes = (): CodeExampleType => { return [ { - lang: 'html', + language: 'html', title: 'HTML', - codes: [ - { - language: 'html', - code: ` -`, - }, - ], + code: `` }, { - lang: 'tsx', + language: 'tsx', title: 'React', - codes: [ - { - language: 'tsx', - code: `import { PascalCase } from 'lucide-react'; + code: `import { PascalCase } from 'lucide-react'; const App = () => { return ( @@ -45,17 +33,11 @@ const App = () => { export default App; `, - - }, - ], }, { - lang: 'vue', - title: 'Vue 3', - codes: [ - { - language: 'vue', - code: ` @@ -63,33 +45,21 @@ export default App; `, - - }, - ], }, { - lang: 'svelte', + language: 'svelte', title: 'Svelte', - codes: [ - { - language: 'svelte', - code: ` `, - - }, - ], }, { - lang: 'preact', + language: 'tsx', title: 'Preact', - codes: [ - { - language: 'tsx', - code: `import { PascalCase } from 'lucide-preact'; + code: `import { PascalCase } from 'lucide-preact'; const App = () => { return ( @@ -99,17 +69,11 @@ const App = () => { export default App; `, - - }, - ], }, { - lang: 'solid', + language: 'tsx', title: 'Solid', - codes: [ - { - language: 'tsx', - code: `import { PascalCase } from 'lucide-solid'; + code: `import { PascalCase } from 'lucide-solid'; const App = () => { return ( @@ -119,17 +83,11 @@ const App = () => { export default App; `, - - }, - ], }, { - lang: 'angular', + language: 'tsx', title: 'Angular', - codes: [ - { - language: 'tsx', - code: `// app.module.ts + code: `// app.module.ts import { LucideAngularModule, PascalCase } from 'lucide-angular'; @NgModule({ @@ -141,54 +99,38 @@ import { LucideAngularModule, PascalCase } from 'lucide-angular'; // app.component.html `, - }, - ], }, { - lang: 'html', + language: 'html', title: 'Icon Font', - codes: [ - { - language: 'html', - code: `
`, - }, - ], - }, - { - lang: 'dart', - title: 'Flutter', - codes: [ - { - language: 'dart', - code: `Icon(LucideIcons.Name); -`, - }, - ], - }, + } ] } export type ThemeOptions = - | IThemeRegistration - | { light: IThemeRegistration; dark: IThemeRegistration } + | ThemeRegistration + | { light: ThemeRegistration; dark: ThemeRegistration } const highLightCode = async (code: string, lang: string, active?: boolean) => { const highlighter = await getHighlighter({ - themes: ['material-theme-palenight'], - langs: [...BUNDLED_LANGUAGES], - processors: [] + themes: ['github-light', 'github-dark'], + langs: Object.keys(bundledLanguages) }) const highlightedCode = highlighter.codeToHtml(code, { lang, - // lineOptions, - theme: 'material-theme-palenight' - }).replace('background-color: #292D3E', '') + themes: { + light: 'github-light', + dark: 'github-dark' + }, + defaultColor: false + }).replace('shiki-themes', 'shiki-themes vp-code') return `
@@ -201,16 +143,15 @@ const highLightCode = async (code: string, lang: string, active?: boolean) => { export default async function createCodeExamples() { const codes = getIconCodes(); - const codeExamplePromises = codes.map(async (codeTemplate, index) => { - const { title, lang, codes } = codeTemplate; + const codeExamplePromises = codes.map(async ({ title, language, code }, index) => { const isFirst = index === 0; - const code = await highLightCode(codes[0].code, codes[0].language || lang, isFirst); + const codeString = await highLightCode(code, language, isFirst); return { title, - language: codes[0].language || lang, - code, + language: language, + code: codeString, }; }) diff --git a/docs/.vitepress/sidebar.ts b/docs/.vitepress/sidebar.ts index 168c6be12..d7eec57a5 100644 --- a/docs/.vitepress/sidebar.ts +++ b/docs/.vitepress/sidebar.ts @@ -10,28 +10,53 @@ const sidebar: UserConfig['themeConfig']['sidebar'] = { { text: 'Comparison', link: '/guide/comparison' } ] }, - // { - // text: 'Using Icons', - // items: [ - // { - // text: 'How to use icons', - // link: 'how-to-use-icons' - // }, - // { - // text: 'Styling icons', - // link: 'styling-icons' - // }, - // { - // text: 'Accessibility', - // link: 'accessibility' - // }, - // { - // text: 'What should I use', - // link: 'what-should-i-use' - // }, - - // ] - // }, + { + text: 'Basics', + items: [ + { + text: 'Color', + link: '/guide/basics/color' + }, + { + text: 'Sizing', + link: '/guide/basics/sizing' + }, + { + text: 'Stroke width', + link: '/guide/basics/stroke-width' + }, + ] + }, + // TODO: Add this section + { + text: 'Advanced', + items: [ + // { + // text: 'Accessibility', + // link: '/guide/advanced/accessibility' + // }, + { + text: 'Global styling', + link: '/guide/advanced/global-styling' + }, + // { + // text: 'Animations', + // }, + { + text: 'Filled icons', + link: '/guide/advanced/filled-icons' + }, + // { + // text: 'Combining icons', + // }, + // { + // text: 'Dynamic imports' + // }, + // { + // text: 'Auto importing' + // }, + ] + }, { text: 'Packages', items: [ @@ -103,15 +128,9 @@ const sidebar: UserConfig['themeConfig']['sidebar'] = { ] }, ], + // This should be here to keep the sidebar shown on the icons page 'icons': [ { text: '', link: '/' }, - // { text: 'Categorized', link: '/icons/categorized' }, - // { - // text: 'Categories', - // items: [ - // ...(getAllCategoryFiles().map((category) => ({ text: category, link: `/icons/category/${category}` }))) - // ] - // } ], } diff --git a/docs/.vitepress/theme/components/PageContainer.vue b/docs/.vitepress/theme/components/PageContainer.vue index 2a4aa553e..4c8201b3e 100644 --- a/docs/.vitepress/theme/components/PageContainer.vue +++ b/docs/.vitepress/theme/components/PageContainer.vue @@ -7,5 +7,6 @@ diff --git a/docs/.vitepress/theme/components/base/Badge.vue b/docs/.vitepress/theme/components/base/Badge.vue index 146309f53..2bf52174e 100644 --- a/docs/.vitepress/theme/components/base/Badge.vue +++ b/docs/.vitepress/theme/components/base/Badge.vue @@ -36,6 +36,7 @@ const component = computed(() => props.href ? 'a' : 'div') border-radius: 6px; background-color: var(--vp-c-bg-alt); color: var(--vp-c-text-1); + text-decoration: none; /* width: 56px; height: 56px; */ font-size: 16px; diff --git a/docs/.vitepress/theme/components/base/Card.vue b/docs/.vitepress/theme/components/base/Card.vue new file mode 100644 index 000000000..23500f5a6 --- /dev/null +++ b/docs/.vitepress/theme/components/base/Card.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/docs/.vitepress/theme/components/base/CardGrid.vue b/docs/.vitepress/theme/components/base/CardGrid.vue new file mode 100644 index 000000000..9da66008d --- /dev/null +++ b/docs/.vitepress/theme/components/base/CardGrid.vue @@ -0,0 +1,36 @@ + + + diff --git a/docs/.vitepress/theme/components/base/ColorPicker.vue b/docs/.vitepress/theme/components/base/ColorPicker.vue index 1f93d3624..40ded8f48 100644 --- a/docs/.vitepress/theme/components/base/ColorPicker.vue +++ b/docs/.vitepress/theme/components/base/ColorPicker.vue @@ -54,7 +54,7 @@ const value = computed({ flex-shrink: 0; } .color-picker { - background: var(--color-picker-bg, var(--vp-c-bg-soft)); + background: var(--color-picker-bg, var(--vp-c-bg-alt)); border-radius: 8px; color: var(--vp-c-text-2); padding: 4px 8px; @@ -71,10 +71,10 @@ const value = computed({ .color-input-text { width: 100%; height: 100%; - padding: 0 8px; + padding: 0 0 0 8px; border: none; background: transparent; - color: var(--vp-c-text-2); + color: var(--vp-c-text-1); font-size: 14px; text-align: left; border-radius: 8px; diff --git a/docs/.vitepress/theme/components/base/GridSection.vue b/docs/.vitepress/theme/components/base/GridSection.vue new file mode 100644 index 000000000..3b5da22be --- /dev/null +++ b/docs/.vitepress/theme/components/base/GridSection.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/docs/.vitepress/theme/components/base/RangeSlider.vue b/docs/.vitepress/theme/components/base/RangeSlider.vue index fd34ebc4b..bfef6d091 100644 --- a/docs/.vitepress/theme/components/base/RangeSlider.vue +++ b/docs/.vitepress/theme/components/base/RangeSlider.vue @@ -49,7 +49,7 @@ const percentage = computed(() => `${((Number(props.modelValue) - props. width: 100%; line-height: 10px; height: 20px; - --bar-color: var(--slider-bar-color, var(--vp-c-bg-soft)); + --bar-color: var(--slider-bar-color, var(--vp-input-switch-bg-color)); } .slider:hover input{ diff --git a/docs/.vitepress/theme/components/home/HomeHeroBefore.vue b/docs/.vitepress/theme/components/home/HomeHeroBefore.vue index 52d078f25..9754ad365 100644 --- a/docs/.vitepress/theme/components/home/HomeHeroBefore.vue +++ b/docs/.vitepress/theme/components/home/HomeHeroBefore.vue @@ -10,7 +10,7 @@ import { data } from './HomeHeroBefore.data' :href="`https://github.com/lucide-icons/lucide/releases/tag/${data.version}`" target="_blank" rel="noreferrer noopener" - >{{ data.version }} + >v{{ data.version }} diff --git a/docs/.vitepress/theme/components/home/HomeIconCustomizer.vue b/docs/.vitepress/theme/components/home/HomeIconCustomizer.vue index 53e13f14f..14dc77550 100644 --- a/docs/.vitepress/theme/components/home/HomeIconCustomizer.vue +++ b/docs/.vitepress/theme/components/home/HomeIconCustomizer.vue @@ -67,7 +67,10 @@ watch(absoluteStrokeWidth, (enabled) => { Lucide has a lot of customization options to match the icons with your UI.

-
+
-import { onMounted } from 'vue' -import { useCategoryView } from '../../composables/useCategoryView' +import { onMounted } from 'vue'; +import { useCategoryView } from '../../composables/useCategoryView'; interface Header { - level: number - title: string - slug: string - iconCount: number - link: string - children: Header[] + level: number; + title: string; + slug: string; + iconCount: number; + link: string; + children: Header[]; } type MenuItem = Omit & { - children?: MenuItem[] -} + children?: MenuItem[]; +}; const props = defineProps<{ - headers: MenuItem[] - root?: boolean -}>() + headers: MenuItem[]; + root?: boolean; +}>(); -const { selectedCategory } = useCategoryView() +const { selectedCategory } = useCategoryView(); function onClick(event: Event) { - const target = (event.target as HTMLElement).nodeName === 'span' ? (event.target as HTMLElement).parentNode : event.target as HTMLElement - const id = '#' + (target as HTMLAnchorElement).href!.split('#')[1] - const decodedId = decodeURIComponent(id) + const target = + (event.target as HTMLElement).nodeName === 'span' + ? (event.target as HTMLElement).parentNode + : (event.target as HTMLElement); + const href = (target as HTMLAnchorElement)?.href; - selectedCategory.value = decodedId.replace('#', '') + if (href) { + const id = '#' + href.split('#')[1]; + const decodedId = decodeURIComponent(id); - const heading = document.querySelector(decodedId) - heading?.focus() + selectedCategory.value = decodedId.replace('#', ''); + + const heading = document.querySelector(decodedId); + heading?.focus(); + } }