diff --git a/.cspell/custom-words.txt b/.cspell/custom-words.txt new file mode 100644 index 0000000000..056ab5a511 --- /dev/null +++ b/.cspell/custom-words.txt @@ -0,0 +1,17 @@ +# Custom words +fullscreen +gamepad +gantt +kanban +pilcrow +squircle +strikethrough +touchpad +ungroup +pilcrow +toc + +# Brands +codepen +codesandbox +dribbble diff --git a/.eslintignore b/.eslintignore index 925b234bd2..ffae92223d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,6 +6,5 @@ tests node_modules .eslintrc.js docs/images -docs/guide/basics/examples -docs/guide/advanced/examples +docs/**/examples/ packages/lucide-react/dynamicIconImports.js diff --git a/.eslintrc.js b/.eslintrc.js index ff086ba210..ae2e17576a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,4 +1,4 @@ -const DEFAULT_ATTRS = require('./scripts/render/default-attrs.json'); +const DEFAULT_ATTRS = require('./tools/build-icons/render/default-attrs.json'); module.exports = { root: true, @@ -15,7 +15,9 @@ module.exports = { 'no-use-before-define': 'off', 'import/no-extraneous-dependencies': [ 'error', - { devDependencies: ['**/*.test.js', '**/*.spec.js', './scripts/**'] }, + { + devDependencies: ['**/*.test.js', '**/*.spec.js', '**/scripts/**'], + }, ], 'import/extensions': [ 'error', @@ -42,12 +44,15 @@ module.exports = { '@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) })) + '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-multiple-empty-lines': ['error', { max: 0 }], '@html-eslint/no-extra-spacing-attrs': [ 'error', { @@ -64,7 +69,7 @@ module.exports = { '@html-eslint/element-newline': 'error', '@html-eslint/no-trailing-spaces': 'error', '@html-eslint/quotes': 'error', - } + }, }, ], }; diff --git a/.github/ISSUE_TEMPLATE/02_bug_report.yml b/.github/ISSUE_TEMPLATE/02_bug_report.yml index 4d830cd558..21dd4e1f2c 100644 --- a/.github/ISSUE_TEMPLATE/02_bug_report.yml +++ b/.github/ISSUE_TEMPLATE/02_bug_report.yml @@ -35,6 +35,16 @@ body: placeholder: e.g. 0.289.1 validations: required: true + - type: checkboxes + id: can-reproduce-in-latest-version + attributes: + label: Can you reproduce this in the latest version? + description: i.e. after running `npm install lucide-react@latest` + options: + - label: 'Yes' + - label: 'No' + validations: + required: false - type: checkboxes id: browsers attributes: @@ -59,6 +69,9 @@ body: - label: Windows - label: Linux - label: macOS + - label: ChromeOS + - label: iOS + - label: Android - label: Other/not relevant - type: textarea id: description diff --git a/.github/ISSUE_TEMPLATE/03_bug_report_site.yml b/.github/ISSUE_TEMPLATE/03_bug_report_site.yml index 36438cf439..8b1cda4afc 100644 --- a/.github/ISSUE_TEMPLATE/03_bug_report_site.yml +++ b/.github/ISSUE_TEMPLATE/03_bug_report_site.yml @@ -30,6 +30,9 @@ body: - label: Windows - label: Linux - label: macOS + - label: ChromeOS + - label: iOS + - label: Android - label: Other/not relevant - type: textarea id: description diff --git a/.github/actions/build-and-test.yml b/.github/actions/build-and-test.yml index 2a0b9fa8d8..5b5a4675f9 100644 --- a/.github/actions/build-and-test.yml +++ b/.github/actions/build-and-test.yml @@ -1,5 +1,5 @@ -name: "Build and Test" -description: "Builds and test a package" +name: 'Build and Test' +description: 'Builds and test a package' inputs: name: @@ -7,7 +7,7 @@ inputs: required: true runs: - using: "composite" + using: 'composite' steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 diff --git a/.github/actions/check-icons.yml b/.github/actions/check-icons.yml index ad9af1f9ef..0850a17ac9 100644 --- a/.github/actions/check-icons.yml +++ b/.github/actions/check-icons.yml @@ -1,5 +1,5 @@ -name: "Check icons" -description: "Cross-checks icon and category references in JSON descriptors" +name: 'Check icons' +description: 'Cross-checks icon and category references in JSON descriptors' inputs: name: @@ -7,7 +7,7 @@ inputs: required: true runs: - using: "composite" + using: 'composite' steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 diff --git a/.github/labeler.yml b/.github/labeler.yml index 696122eff8..2023caf3d1 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,92 +1,92 @@ # For changed dependencies ๐Ÿ“ฆ dependencies: -- changed-files: - - any-glob-to-any-file: - - pnpm-lock.yaml + - changed-files: + - any-glob-to-any-file: + - pnpm-lock.yaml # For changes in documentation ๐Ÿ“– documentation: -- changed-files: - - any-glob-to-any-file: - - 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: -- changed-files: - - any-glob-to-any-file: - - 'docs/**' + - changed-files: + - any-glob-to-any-file: + - 'docs/**' # For changes in the metadata ๐Ÿซง metadata: -- changed-files: - - any-glob-to-any-file: - - 'icons/*.json' - - categories/* + - changed-files: + - any-glob-to-any-file: + - 'icons/*.json' + - categories/* # For changes or added icons ๐ŸŽจ icon: -- changed-files: - - any-glob-to-any-file: - - 'icons/*.svg' + - changed-files: + - any-glob-to-any-file: + - 'icons/*.svg' # For changes in the lucide package ๐Ÿงณ lucide package: -- changed-files: - - any-glob-to-any-file: - - 'packages/lucide/*' + - changed-files: + - any-glob-to-any-file: + - 'packages/lucide/*' # For changes in the lucide React package โš›๏ธ react package: -- changed-files: - - any-glob-to-any-file: - - 'packages/lucide-react/*' + - changed-files: + - any-glob-to-any-file: + - 'packages/lucide-react/*' # For changes in the lucide React Native package โš›๏ธ react native package: -- changed-files: - - any-glob-to-any-file: - - 'packages/lucide-react-native/*' + - changed-files: + - any-glob-to-any-file: + - 'packages/lucide-react-native/*' # For changes in the lucide vue packages ๐Ÿ’Ž vue package: -- changed-files: - - any-glob-to-any-file: - - '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: -- changed-files: - - any-glob-to-any-file: - - 'packages/lucide-angular/*' + - changed-files: + - any-glob-to-any-file: + - 'packages/lucide-angular/*' # For changes in the lucide preact package โš›๏ธ preact package: -- changed-files: - - any-glob-to-any-file: - - 'packages/lucide-preact/*' + - changed-files: + - any-glob-to-any-file: + - 'packages/lucide-preact/*' # For changes in the lucide svelte package ๐Ÿงฃ svelte package: -- changed-files: - - any-glob-to-any-file: - - 'packages/lucide-svelte/*' + - changed-files: + - any-glob-to-any-file: + - 'packages/lucide-svelte/*' # For changes in the lucide solid package ๐Ÿช solid package: -- changed-files: - - any-glob-to-any-file: - - 'packages/lucide-solid/*' + - changed-files: + - any-glob-to-any-file: + - 'packages/lucide-solid/*' # For changes in the lucide static package ๐Ÿชจ static package: -- changed-files: - - any-glob-to-any-file: - - 'packages/lucide-static/*' + - 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/*' + - 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 1bda809420..2403cee42c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -47,7 +47,7 @@ - [ ] 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. +- [ ] I've correctly optimized all icons to three points of precision. ## Before Submitting diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b6f6fed5d..b4a8048af2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,8 +17,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v2 - with: - version: 8 - uses: actions/setup-node@v4 with: node-version: 18 diff --git a/.github/workflows/close-issue-with-banned-phrases.yml b/.github/workflows/close-issue-with-banned-phrases.yml new file mode 100644 index 0000000000..135b5c6c28 --- /dev/null +++ b/.github/workflows/close-issue-with-banned-phrases.yml @@ -0,0 +1,35 @@ +name: Close Issue with Banned Phrases + +on: + issues: + types: [opened] + +jobs: + block_phrases: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Check for blocked phrases in issue title + run: | + ISSUE_TITLE=$(jq -r '.issue.title' "$GITHUB_EVENT_PATH") + BLOCKED_PHRASES=("twitter" "whatsapp" "logo" "google" "tiktok" "facebook" "slack" "discord") + + # Check title and body for blocked phrases + for PHRASE in "${BLOCKED_PHRASES[@]}" + do + if echo "$ISSUE_TITLE" | grep -i "$PHRASE"; then + gh issue close ${{ github.event.issue.number }} --reason "not planned" --comment "This looks like a duplicate, use the [search](https://github.com/lucide-icons/lucide/issues?q=is%3Aissue+$PHRASE) to find similar issues. + + Read more about brand guideline rules at [We're not accepting new Brand icons #670](https://github.com/lucide-icons/lucide/issues/670). + + Always happy to help on [Discord](https://discord.gg/EH6nSts)." + gh issue lock ${{ github.event.issue.number }} --reason spam + exit 1 + fi + done + env: + GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/close-stale-prs.yml b/.github/workflows/close-stale-prs.yml index b074a60554..0b47315f47 100644 --- a/.github/workflows/close-stale-prs.yml +++ b/.github/workflows/close-stale-prs.yml @@ -1,11 +1,13 @@ name: Close stale issues and PR on: schedule: - - cron: "45 1 * * *" + - cron: '45 1 * * *' jobs: stale: runs-on: ubuntu-latest + permissions: + pull-requests: write steps: - uses: actions/stale@v9 with: @@ -14,4 +16,5 @@ jobs: close-pr-message: This PR was closed because it has been stalled for 5 days with no activity. close-pr-label: ๐Ÿงถ stale days-before-stale: 30 + days-before-issue-stale: -1 days-before-close: -1 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 2a340279ef..bf474c4787 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -1,6 +1,6 @@ -name: "Pull Request Labeler" +name: 'Pull Request Labeler' on: -- pull_request_target + - pull_request_target jobs: triage: @@ -9,4 +9,4 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v5 + - uses: actions/labeler@v5 diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index cdc76ea5a4..b31306b454 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -11,8 +11,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v2 - with: - version: 8 - uses: actions/setup-node@v4 with: node-version: 18 diff --git a/.github/workflows/lucide-angular.yml b/.github/workflows/lucide-angular.yml index d31c0859b8..143847741a 100644 --- a/.github/workflows/lucide-angular.yml +++ b/.github/workflows/lucide-angular.yml @@ -8,13 +8,11 @@ on: - pnpm-lock.yaml jobs: - lucide-angular: + build: 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 @@ -26,5 +24,18 @@ jobs: - name: Build run: pnpm --filter lucide-angular build + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + - uses: actions/setup-node@v3.8.1 + with: + node-version: 18 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Test run: pnpm --filter lucide-angular test diff --git a/.github/workflows/lucide-font.yml b/.github/workflows/lucide-font.yml index c12be90b6f..d9a771a7b1 100644 --- a/.github/workflows/lucide-font.yml +++ b/.github/workflows/lucide-font.yml @@ -13,8 +13,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v2 - with: - version: 8 - uses: actions/setup-node@v4 with: node-version: 18 @@ -29,7 +27,7 @@ jobs: - name: Create font in ./lucide-font run: pnpm build:font - - name: "Upload to Artifacts" + - name: 'Upload to Artifacts' uses: actions/upload-artifact@v3 with: name: lucide-font diff --git a/.github/workflows/lucide-preact.yml b/.github/workflows/lucide-preact.yml index 0c071d1b6b..95da247577 100644 --- a/.github/workflows/lucide-preact.yml +++ b/.github/workflows/lucide-preact.yml @@ -4,6 +4,7 @@ on: pull_request: paths: - packages/lucide-preact/** + - packages/shared/** - tools/build-icons/** - tools/rollup-plugins/** - pnpm-lock.yaml @@ -14,8 +15,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v2 - with: - version: 8 - uses: actions/setup-node@v4 with: node-version: 18 @@ -24,8 +23,5 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Build - run: pnpm --filter lucide-preact build - - name: Test run: pnpm --filter lucide-preact test diff --git a/.github/workflows/lucide-react-native.yml b/.github/workflows/lucide-react-native.yml index f8be337e10..72806b7305 100644 --- a/.github/workflows/lucide-react-native.yml +++ b/.github/workflows/lucide-react-native.yml @@ -4,6 +4,7 @@ on: pull_request: paths: - packages/lucide-react-native/** + - packages/shared/** - tools/build-icons/** - tools/rollup-plugins/** - pnpm-lock.yaml @@ -14,8 +15,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v2 - with: - version: 8 - uses: actions/setup-node@v4 with: node-version: 18 @@ -24,8 +23,5 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Build - run: pnpm --filter lucide-react-native build - - name: Test run: pnpm --filter lucide-react-native test diff --git a/.github/workflows/lucide-react.yml b/.github/workflows/lucide-react.yml index ff11e530a3..cd25adc0d2 100644 --- a/.github/workflows/lucide-react.yml +++ b/.github/workflows/lucide-react.yml @@ -4,19 +4,18 @@ on: pull_request: paths: - packages/lucide-react/** + - packages/shared/** - tools/build-icons/** - tools/rollup-plugins/** - scripts/generateNextJSAliases.mjs - pnpm-lock.yaml jobs: - lucide-react: + build: 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 @@ -28,5 +27,18 @@ jobs: - name: Build run: pnpm --filter lucide-react build + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + - uses: actions/setup-node@v3.8.1 + with: + node-version: 18 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Test run: pnpm --filter lucide-react test diff --git a/.github/workflows/lucide-shared.yml b/.github/workflows/lucide-shared.yml new file mode 100644 index 0000000000..248ee86138 --- /dev/null +++ b/.github/workflows/lucide-shared.yml @@ -0,0 +1,24 @@ +name: Lucide Shared Checks + +on: + pull_request: + paths: + - packages/shared/** + - pnpm-lock.yaml + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + - uses: actions/setup-node@v3.8.1 + with: + node-version: 18 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Test + run: pnpm --filter lucide-react test diff --git a/.github/workflows/lucide-solid.yml b/.github/workflows/lucide-solid.yml index d32f98a1b5..8abeb75dfe 100644 --- a/.github/workflows/lucide-solid.yml +++ b/.github/workflows/lucide-solid.yml @@ -4,18 +4,17 @@ on: pull_request: paths: - packages/lucide-solid/** + - packages/shared/** - tools/build-icons/** - tools/rollup-plugins/** - pnpm-lock.yaml jobs: - lucide-solid: + build: 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 @@ -27,5 +26,18 @@ jobs: - name: Build run: pnpm --filter lucide-solid build + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + - uses: actions/setup-node@v3.8.1 + with: + node-version: 18 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Test run: pnpm --filter lucide-solid test diff --git a/.github/workflows/lucide-static.yml b/.github/workflows/lucide-static.yml index fc0fc4b99a..02eace8bf5 100644 --- a/.github/workflows/lucide-static.yml +++ b/.github/workflows/lucide-static.yml @@ -13,8 +13,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v2 - with: - version: 8 - uses: actions/setup-node@v4 with: node-version: 18 diff --git a/.github/workflows/lucide-svelte.yml b/.github/workflows/lucide-svelte.yml index 07088c6fb9..7603ce9122 100644 --- a/.github/workflows/lucide-svelte.yml +++ b/.github/workflows/lucide-svelte.yml @@ -4,18 +4,17 @@ on: pull_request: paths: - packages/lucide-svelte/** + - packages/shared/** - tools/build-icons/** - tools/rollup-plugins/** - pnpm-lock.yaml jobs: - lucide-svelte: + build: 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 @@ -27,5 +26,18 @@ jobs: - name: Build run: pnpm --filter lucide-svelte build + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + - uses: actions/setup-node@v3.8.1 + with: + node-version: 18 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Test run: pnpm --filter lucide-svelte test diff --git a/.github/workflows/lucide-vue-next.yml b/.github/workflows/lucide-vue-next.yml index 68fd57a84b..12c39f1a99 100644 --- a/.github/workflows/lucide-vue-next.yml +++ b/.github/workflows/lucide-vue-next.yml @@ -4,18 +4,17 @@ on: pull_request: paths: - packages/lucide-vue-next/** + - packages/shared/** - tools/build-icons/** - tools/rollup-plugins/** - pnpm-lock.yaml jobs: - lucide-vue-next: + build: 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 @@ -27,5 +26,18 @@ jobs: - name: Build run: pnpm --filter lucide-vue-next build + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + - uses: actions/setup-node@v3.8.1 + with: + node-version: 18 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Test run: pnpm --filter lucide-vue-next test diff --git a/.github/workflows/lucide-vue.yml b/.github/workflows/lucide-vue.yml index 94f2052ee9..37b67537ff 100644 --- a/.github/workflows/lucide-vue.yml +++ b/.github/workflows/lucide-vue.yml @@ -4,18 +4,17 @@ on: pull_request: paths: - packages/lucide-vue/** + - packages/shared/** - tools/build-icons/** - tools/rollup-plugins/** - pnpm-lock.yaml jobs: - lucide-vue: + build: 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 @@ -27,5 +26,18 @@ jobs: - name: Build run: pnpm --filter lucide-vue build + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + - uses: actions/setup-node@v3.8.1 + with: + node-version: 18 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Test run: pnpm --filter lucide-vue test diff --git a/.github/workflows/lucide.yml b/.github/workflows/lucide.yml index 369d0a9e1a..1cc8d6f429 100644 --- a/.github/workflows/lucide.yml +++ b/.github/workflows/lucide.yml @@ -9,13 +9,11 @@ on: - pnpm-lock.yaml jobs: - lucide: + build: 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 @@ -27,5 +25,18 @@ jobs: - name: Build run: pnpm --filter lucide build + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + - uses: actions/setup-node@v3.8.1 + with: + node-version: 18 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Test run: pnpm --filter lucide test diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 9c9ba21637..a204412783 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -3,7 +3,7 @@ name: Add Changed Icons comment on: pull_request_target: paths: - - 'icons/*.svg' + - 'icons/*' branches: - main - fix-icon-preview @@ -68,6 +68,16 @@ jobs: # 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 + lint-aliases: + name: Check Uniqueness of Aliases + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + - name: Check Uniqueness of Aliases + run: "! cat <(printf \"%s\\n\" icons/*.json | while read -r name; do basename \"$name\" .json; done) <(jq -cr 'select(.aliases) | .aliases[] | if type==\"string\" then . else .name end' icons/*.json) | sort | uniq -c | grep -ve '^\\s*1 '" + generate-changed-icons-comment: runs-on: ubuntu-latest permissions: @@ -94,6 +104,10 @@ jobs: comment-author: 'github-actions[bot]' body-includes: Added or changed icons + - uses: actions/setup-node@v4 + - name: Install svgson for code preview (safer and faster than installing all deps) + run: npm install svgson + - name: Generate comment markup run: node ./scripts/generateChangedIconsCommentMarkup.mjs >> comment-markup.md id: comment-markup diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f740193058..ceb84ea605 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,22 +41,21 @@ jobs: strategy: fail-fast: false matrix: - package: [ - 'lucide', - 'lucide-react', - 'lucide-react-native', - 'lucide-vue', - 'lucide-vue-next', - 'lucide-angular', - 'lucide-preact', - 'lucide-solid', - 'lucide-svelte', - ] + package: + [ + 'lucide', + 'lucide-react', + 'lucide-react-native', + 'lucide-vue', + 'lucide-vue-next', + 'lucide-angular', + 'lucide-preact', + 'lucide-solid', + 'lucide-svelte', + ] steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v2 - with: - version: 8 - uses: actions/setup-node@v4 with: node-version: 18 @@ -88,8 +87,6 @@ jobs: - uses: actions/checkout@v4 - uses: actions/download-artifact@v3 - uses: pnpm/action-setup@v2 - with: - version: 8 - uses: actions/setup-node@v4 with: node-version: 18 @@ -120,8 +117,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v2 - with: - version: 8 - uses: actions/setup-node@v4 with: node-version: 18 @@ -136,7 +131,7 @@ jobs: - name: Create font in ./lucide-font run: pnpm build:font - - name: "Upload to Artifacts" + - name: 'Upload to Artifacts' uses: actions/upload-artifact@v3 with: name: lucide-font @@ -145,10 +140,7 @@ jobs: post-release: if: github.repository == 'lucide-icons/lucide' runs-on: ubuntu-latest - needs: [ - pre-release, - lucide-font, - ] + needs: [pre-release, lucide-font] steps: - uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index 7820353255..733b45c18c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,9 @@ outlined packages/**/src/icons/*.js packages/**/src/icons/*.ts packages/**/src/icons/*.tsx +packages/**/src/aliases/*.ts packages/**/src/aliases.ts +!packages/**/src/aliases/index.ts packages/**/src/dynamicIconImports.ts packages/**/dynamicIconImports.js packages/**/dynamicIconImports.d.ts @@ -34,6 +36,7 @@ docs/.vitepress/data/iconNodes docs/.vitepress/data/iconMetaData.ts docs/.vitepress/data/releaseMetaData.json docs/.vitepress/data/releaseMetaData +docs/.vitepress/data/categoriesData.json docs/.vitepress/data/iconDetails docs/.vitepress/data/relatedIcons.json docs/.vercel diff --git a/.prettierignore b/.prettierignore index 943334f5ec..c150f271bb 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,12 @@ pnpm-lock.yaml +# docs examples +docs/**/examples/ +docs/.vitepress/.temp +docs/.vitepress/cache +docs/.vitepress/data +docs/.nitro + # lucide-angular packages/lucide-angular/.angular/cache diff --git a/.vscode/launch.json b/.vscode/launch.json index b23d58751f..177bc7dfad 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,4 +12,4 @@ "webRoot": "${workspaceFolder}" } ] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 59e014f602..f179cbc079 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,13 +1,6 @@ { - "cSpell.words": [ - "devs", - "preact", - "Preact" - ], + "cSpell.words": ["devs", "preact", "Preact"], "eslint.enable": true, - "eslint.validate": [ - "javascript", - "svg" - ], + "eslint.validate": ["javascript", "svg"], "svg.preview.background": "transparent" } diff --git a/.vscode/svg.code-snippets b/.vscode/svg.code-snippets index 3c491c3b87..52cbb23c1a 100644 --- a/.vscode/svg.code-snippets +++ b/.vscode/svg.code-snippets @@ -49,7 +49,7 @@ "circle", "" + "body": "" }, "Ellipse": { "scope": "xml", diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 90d5a685d5..ebce76e413 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,6 +39,8 @@ You can also [download an Adobe Illustrator template](https://github.com/lucide- #### [Figma Guide](https://lucide.dev/docs/figma-guide) +#### [Affinity Designer Guide](https://lucide.dev/guide/design/affinity-designer-guide) + ### Submitting Multiple Icons If you want submit multiple icons, please separate the icons and group them. That makes reviewing the icons easier and keep the thread clean and scoped. @@ -70,7 +72,7 @@ pnpm install # Install dependencies, including the workspace packages ### Packages -> PNPM Workspaces -To distribute different packages we use PNPM workspaces. Before you start make sure you are familiar with this concept. The concept of working in workspaces is created by Yarn, they have a well written introduction: [yarn workspaces](https://classic.yarnpkg.com/lang/enhttps://lucide.dev/docs/workspaces). +To distribute different packages we use [PNPM workspaces](https://pnpm.io/workspaces). Before you start make sure you are familiar with this concept. The concept of working in workspaces is created by Yarn, they have a well written introduction: [yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces). The configured directory for workspaces is the [packages](https://github.com/lucide-icons/lucide/tree/main/packages) directory, located in the root directory. There you will find all the current packages from lucide. There are more workspaces defined, see [`pnpm-workspace.yaml`](https://github.com/lucide-icons/lucide/blob/main/pnpm-workspace.yaml). diff --git a/README.md b/README.md index 81235994f3..6076562c33 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ -

Lucide Logo

+

+ + Lucide - Beautiful & consistent icon toolkit made by the community. Open-source project and a fork of Feather Icons. + + + Lucide - Beautiful & consistent icon toolkit made by the community. Open-source project and a fork of Feather Icons. + +

license npm package @@ -274,9 +281,12 @@ Thank you to all the people who contributed to Lucide! ## Sponsors - Powered by Vercel DigitalOcean Referral Badge + +### Awesome backer ๐Ÿบ + +Scipress sponsor badge diff --git a/categories/accessibility.json b/categories/accessibility.json index 95da8cf053..a668d3ee35 100644 --- a/categories/accessibility.json +++ b/categories/accessibility.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Accessibility", "icon": "accessibility" -} \ No newline at end of file +} diff --git a/categories/account.json b/categories/account.json index 284b764cba..95a80ad403 100644 --- a/categories/account.json +++ b/categories/account.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Accounts & access", "icon": "user" -} \ No newline at end of file +} diff --git a/categories/animals.json b/categories/animals.json index 251c93a813..85fafda5f9 100644 --- a/categories/animals.json +++ b/categories/animals.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Animals", "icon": "dog" -} \ No newline at end of file +} diff --git a/categories/arrows.json b/categories/arrows.json index 5b84413b68..3258378f94 100644 --- a/categories/arrows.json +++ b/categories/arrows.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Arrows", "icon": "arrow-left-right" -} \ No newline at end of file +} diff --git a/categories/brands.json b/categories/brands.json index 5ff30efca6..c48c4b3b0c 100644 --- a/categories/brands.json +++ b/categories/brands.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Brands", "icon": "facebook" -} \ No newline at end of file +} diff --git a/categories/buildings.json b/categories/buildings.json index 71df7f6eac..13f24b08f7 100644 --- a/categories/buildings.json +++ b/categories/buildings.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Buildings", "icon": "building" -} \ No newline at end of file +} diff --git a/categories/charts.json b/categories/charts.json index 6f94a14bd7..e9bdc18825 100644 --- a/categories/charts.json +++ b/categories/charts.json @@ -1,5 +1,5 @@ { "$schema": "../category.schema.json", "title": "Charts", - "icon": "pie-chart" -} \ No newline at end of file + "icon": "chart-pie" +} diff --git a/categories/communication.json b/categories/communication.json index 7fd8bb175f..3b9a899d74 100644 --- a/categories/communication.json +++ b/categories/communication.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Communication", "icon": "message-circle" -} \ No newline at end of file +} diff --git a/categories/connectivity.json b/categories/connectivity.json index 17b4c7bee3..b566a84ec9 100644 --- a/categories/connectivity.json +++ b/categories/connectivity.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Connectivity", "icon": "wifi" -} \ No newline at end of file +} diff --git a/categories/currency.json b/categories/currency.json index 31e2846fbe..0879774d1c 100644 --- a/categories/currency.json +++ b/categories/currency.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Currency", "icon": "dollar-sign" -} \ No newline at end of file +} diff --git a/categories/cursors.json b/categories/cursors.json index 16dddb96a3..9522faa164 100644 --- a/categories/cursors.json +++ b/categories/cursors.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Cursors", "icon": "mouse-pointer-2" -} \ No newline at end of file +} diff --git a/categories/design.json b/categories/design.json index 312c2048d6..e32592560a 100644 --- a/categories/design.json +++ b/categories/design.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Design", "icon": "palette" -} \ No newline at end of file +} diff --git a/categories/development.json b/categories/development.json index cfbe175917..113fd1cc21 100644 --- a/categories/development.json +++ b/categories/development.json @@ -1,5 +1,5 @@ { "$schema": "../category.schema.json", "title": "Coding & development", - "icon": "code-2" + "icon": "code-xml" } diff --git a/categories/devices.json b/categories/devices.json index cb2639fc30..3bb54f1807 100644 --- a/categories/devices.json +++ b/categories/devices.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Devices", "icon": "smartphone" -} \ No newline at end of file +} diff --git a/categories/emoji.json b/categories/emoji.json index 14afb25abb..78442db1da 100644 --- a/categories/emoji.json +++ b/categories/emoji.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Emoji", "icon": "smile" -} \ No newline at end of file +} diff --git a/categories/files.json b/categories/files.json index 4d1fa3ad94..073a09beb3 100644 --- a/categories/files.json +++ b/categories/files.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "File icons", "icon": "panels-top-left" -} \ No newline at end of file +} diff --git a/categories/food-beverage.json b/categories/food-beverage.json index 2b67eaeeab..7456558b62 100644 --- a/categories/food-beverage.json +++ b/categories/food-beverage.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Food & beverage", "icon": "coffee" -} \ No newline at end of file +} diff --git a/categories/furniture.json b/categories/furniture.json index 27a03034aa..56cf3fa300 100644 --- a/categories/furniture.json +++ b/categories/furniture.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Furniture", "icon": "rocking-chair" -} \ No newline at end of file +} diff --git a/categories/gaming.json b/categories/gaming.json index 9bf0a6daa9..2c90f4c9d4 100644 --- a/categories/gaming.json +++ b/categories/gaming.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Gaming", "icon": "gamepad-2" -} \ No newline at end of file +} diff --git a/categories/home.json b/categories/home.json index e11aae4121..20a89330d1 100644 --- a/categories/home.json +++ b/categories/home.json @@ -1,5 +1,5 @@ { "$schema": "../category.schema.json", "title": "Home", - "icon": "home" -} \ No newline at end of file + "icon": "house" +} diff --git a/categories/layout.json b/categories/layout.json index 034ea4be91..22a163f95e 100644 --- a/categories/layout.json +++ b/categories/layout.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Layout", "icon": "panels-top-left" -} \ No newline at end of file +} diff --git a/categories/mail.json b/categories/mail.json index 4ebda7c34b..3dd1d30267 100644 --- a/categories/mail.json +++ b/categories/mail.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Mail", "icon": "mail" -} \ No newline at end of file +} diff --git a/categories/maps.json b/categories/maps.json index 4832c5f971..1a948f1196 100644 --- a/categories/maps.json +++ b/categories/maps.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Maps", "icon": "map" -} \ No newline at end of file +} diff --git a/categories/maths.json b/categories/maths.json index f32aed6dc2..f80691908f 100644 --- a/categories/maths.json +++ b/categories/maths.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Maths", "icon": "divide" -} \ No newline at end of file +} diff --git a/categories/medical.json b/categories/medical.json index 05ee123246..e745df6978 100644 --- a/categories/medical.json +++ b/categories/medical.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Medical", "icon": "heart" -} \ No newline at end of file +} diff --git a/categories/money.json b/categories/money.json index 2d9eadc558..fd706dee54 100644 --- a/categories/money.json +++ b/categories/money.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Money", "icon": "piggy-bank" -} \ No newline at end of file +} diff --git a/categories/multimedia.json b/categories/multimedia.json index 2c523a7126..00897c3e4d 100644 --- a/categories/multimedia.json +++ b/categories/multimedia.json @@ -1,5 +1,5 @@ { "$schema": "../category.schema.json", "title": "Multimedia", - "icon": "play-circle" -} \ No newline at end of file + "icon": "circle-play" +} diff --git a/categories/nature.json b/categories/nature.json index 836f45f081..aa41fd8dde 100644 --- a/categories/nature.json +++ b/categories/nature.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Nature", "icon": "sprout" -} \ No newline at end of file +} diff --git a/categories/navigation.json b/categories/navigation.json index 78f0cc7f29..0cb9e209dc 100644 --- a/categories/navigation.json +++ b/categories/navigation.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Navigation", "icon": "compass" -} \ No newline at end of file +} diff --git a/categories/notifications.json b/categories/notifications.json index be39626c77..8ef569fd08 100644 --- a/categories/notifications.json +++ b/categories/notifications.json @@ -1,5 +1,5 @@ { "$schema": "../category.schema.json", "title": "Notifications", - "icon": "alert-triangle" -} \ No newline at end of file + "icon": "triangle-alert" +} diff --git a/categories/people.json b/categories/people.json index 15dc4c8ae0..be028e4b5b 100644 --- a/categories/people.json +++ b/categories/people.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "People", "icon": "person-standing" -} \ No newline at end of file +} diff --git a/categories/photography.json b/categories/photography.json index 4d3ffe3b37..a4ad5bff19 100644 --- a/categories/photography.json +++ b/categories/photography.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Photography", "icon": "camera" -} \ No newline at end of file +} diff --git a/categories/science.json b/categories/science.json index 889b7c2069..720983fb3e 100644 --- a/categories/science.json +++ b/categories/science.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Science", "icon": "flask-conical" -} \ No newline at end of file +} diff --git a/categories/seasons.json b/categories/seasons.json index 3a2a7a1e0c..0c4fff3ccc 100644 --- a/categories/seasons.json +++ b/categories/seasons.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Seasons", "icon": "leaf" -} \ No newline at end of file +} diff --git a/categories/security.json b/categories/security.json index 00371462b6..8372554fec 100644 --- a/categories/security.json +++ b/categories/security.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Security", "icon": "shield" -} \ No newline at end of file +} diff --git a/categories/shapes.json b/categories/shapes.json index ed4434f8eb..c968b222be 100644 --- a/categories/shapes.json +++ b/categories/shapes.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Shapes", "icon": "triangle" -} \ No newline at end of file +} diff --git a/categories/shopping.json b/categories/shopping.json index b224f2312c..5e6b05ff5f 100644 --- a/categories/shopping.json +++ b/categories/shopping.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Shopping", "icon": "shopping-bag" -} \ No newline at end of file +} diff --git a/categories/social.json b/categories/social.json index 4c8fee7f88..ee87f85746 100644 --- a/categories/social.json +++ b/categories/social.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Social", "icon": "thumbs-up" -} \ No newline at end of file +} diff --git a/categories/sports.json b/categories/sports.json index 7c3cb01350..25440ad6f2 100644 --- a/categories/sports.json +++ b/categories/sports.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Sports", "icon": "type" -} \ No newline at end of file +} diff --git a/categories/sustainability.json b/categories/sustainability.json index 1c22a5e85b..a0730f6fac 100644 --- a/categories/sustainability.json +++ b/categories/sustainability.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Sustainability", "icon": "recycle" -} \ No newline at end of file +} diff --git a/categories/time.json b/categories/time.json index 75f119816d..2b85210dee 100644 --- a/categories/time.json +++ b/categories/time.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Time & calendar", "icon": "calendar" -} \ No newline at end of file +} diff --git a/categories/tools.json b/categories/tools.json index 456213ad57..48cf0a96a7 100644 --- a/categories/tools.json +++ b/categories/tools.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Tools", "icon": "hammer" -} \ No newline at end of file +} diff --git a/categories/transportation.json b/categories/transportation.json index 0515475d32..984da071c5 100644 --- a/categories/transportation.json +++ b/categories/transportation.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Transportation", "icon": "train-front" -} \ No newline at end of file +} diff --git a/categories/travel.json b/categories/travel.json index a85cd27c49..3d78c06e33 100644 --- a/categories/travel.json +++ b/categories/travel.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Travel", "icon": "backpack" -} \ No newline at end of file +} diff --git a/categories/weather.json b/categories/weather.json index faa9562392..a08cc5d661 100644 --- a/categories/weather.json +++ b/categories/weather.json @@ -2,4 +2,4 @@ "$schema": "../category.schema.json", "title": "Weather", "icon": "cloud-sun" -} \ No newline at end of file +} diff --git a/packages/lucide-figma/src/components/EditBar/index.ts b/comment-markup.md similarity index 100% rename from packages/lucide-figma/src/components/EditBar/index.ts rename to comment-markup.md diff --git a/cspell.json b/cspell.json new file mode 100644 index 0000000000..b98d751c09 --- /dev/null +++ b/cspell.json @@ -0,0 +1,10 @@ +{ + "dictionaries": ["en-us", "custom-words"], + "dictionaryDefinitions": [ + { + "name": "custom-words", + "path": "./.cspell/custom-words.txt", + "addWords": true + } + ] +} diff --git a/docs/.vitepress/api/categories/index.get.ts b/docs/.vitepress/api/categories/index.get.ts index a1fb097cae..76e1f95def 100644 --- a/docs/.vitepress/api/categories/index.get.ts +++ b/docs/.vitepress/api/categories/index.get.ts @@ -1,11 +1,10 @@ -import { eventHandler, setResponseHeader } from 'h3' -import iconMetaData from '../../data/iconMetaData' +import iconMetaData from '../../data/iconMetaData'; export default eventHandler((event) => { - setResponseHeader(event, 'Cache-Control', 'public, max-age=86400') - setResponseHeader(event, 'Access-Control-Allow-Origin', '*') + setResponseHeader(event, 'Cache-Control', 'public, max-age=86400'); + setResponseHeader(event, 'Access-Control-Allow-Origin', '*'); return Object.fromEntries( - Object.entries(iconMetaData).map(([name, { categories }]) => [ name, categories ]) - ) -}) + Object.entries(iconMetaData).map(([name, { categories }]) => [name, categories]), + ); +}); diff --git a/docs/.vitepress/api/figma/data.ts b/docs/.vitepress/api/figma/data.ts new file mode 100644 index 0000000000..1c938c998a --- /dev/null +++ b/docs/.vitepress/api/figma/data.ts @@ -0,0 +1,40 @@ +import iconNodes from '../../data/iconNodes/index.ts'; +import { IconNodeWithKeys } from '../../theme/types'; +import iconMetaData from '../../data/iconMetaData'; +import releaseMeta from '../../data/releaseMetaData.json'; +import categories from '../../data/categoriesData.json'; + +const dataResponse = { + icons: Object.entries(iconNodes).reduce((acc, [name, iconNode]) => { + const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs }]) => { + return [name, attrs]; + }); + + acc[name] = { + iconNode: newIconNode, + aliases: (iconMetaData[name]?.aliases ?? []).map((alias) => + typeof alias === 'string' ? alias : alias.name, + ), + tags: iconMetaData[name].tags ?? [], + categories: iconMetaData[name].categories ?? [], + ...releaseMeta[name], + }; + + return acc; + }, {}), + aliases: Object.entries(iconNodes).reduce((acc, [name]) => { + for (const alias of iconMetaData[name]?.aliases ?? []) { + acc[typeof alias === 'string' ? alias : alias.name] = name; + } + + return acc; + }, {}), + categories, +}; + +export default eventHandler((event) => { + setResponseHeader(event, 'Cache-Control', 'public, max-age=86400'); + setResponseHeader(event, 'Access-Control-Allow-Origin', '*'); + + return dataResponse; +}); diff --git a/docs/.vitepress/api/gh-icon/[...data].get.ts b/docs/.vitepress/api/gh-icon/[...data].get.ts index eacfb498f8..def248303a 100644 --- a/docs/.vitepress/api/gh-icon/[...data].get.ts +++ b/docs/.vitepress/api/gh-icon/[...data].get.ts @@ -13,7 +13,10 @@ export default eventHandler((event) => { const data = pathData.at(-1).slice(0, -4); const [name] = pathData; - const src = Buffer.from(data, 'base64').toString('utf8'); + const src = Buffer.from(data, 'base64') + .toString('utf8') + .replaceAll('\n', '') + .replace(/]*>|<\/svg>/g, ''); const children = []; @@ -25,25 +28,25 @@ export default eventHandler((event) => { .map((_, idx, arr) => arr.slice(0, idx + 1).join('-')) .reverse() .find((groupName) => groupName in iconNodes); - if (backdropName) { + if (!(name in iconNodes) && backdropName) { const iconNode = iconNodes[backdropName]; const LucideIcon = createLucideIcon(backdropName, iconNode); const svg = renderToStaticMarkup(createElement(LucideIcon)); - const backdropString = svg.replace(/]*>|<\/svg>/g, ''); + const backdropString = svg.replaceAll('\n', '').replace(/]*>|<\/svg>/g, ''); children.push( createElement(Backdrop, { backdropString, src, - color: name in iconNodes ? 'red' : '#777', - }) + color: '#777', + }), ); } const svg = Buffer.from( // We can't use jsx here, is not supported here by nitro. - renderToString(createElement(SvgPreview, { src, showGrid: true }, children)) + renderToString(createElement(SvgPreview, { src, showGrid: true }, children)), ).toString('utf8'); defaultContentType(event, 'image/svg+xml'); diff --git a/docs/.vitepress/api/gh-icon/diff/[...data].get.ts b/docs/.vitepress/api/gh-icon/diff/[...data].get.ts new file mode 100644 index 0000000000..b1b0f09c64 --- /dev/null +++ b/docs/.vitepress/api/gh-icon/diff/[...data].get.ts @@ -0,0 +1,37 @@ +import { eventHandler, setResponseHeader, defaultContentType } from 'h3'; +import { renderToString, renderToStaticMarkup } from 'react-dom/server'; +import { createElement } from 'react'; +import Diff from '../../../lib/SvgPreview/Diff.tsx'; +import iconNodes from '../../../data/iconNodes'; +import createLucideIcon from 'lucide-react/src/createLucideIcon'; + +export default eventHandler((event) => { + const { params } = event.context; + + const pathData = params.data.split('/'); + const data = pathData.at(-1).slice(0, -4); + const [name] = pathData; + + const newSrc = Buffer.from(data, 'base64') + .toString('utf8') + .replaceAll('\n', '') + .replace(/]*>|<\/svg>/g, ''); + + const children = []; + + const oldSrc = iconNodes[name] + ? renderToStaticMarkup(createElement(createLucideIcon(name, iconNodes[name]))) + .replaceAll('\n', '') + .replace(/]*>|<\/svg>/g, '') + : ''; + + const svg = Buffer.from( + // We can't use jsx here, is not supported here by nitro. + renderToString(createElement(Diff, { oldSrc, newSrc, showGrid: true }, children)), + ).toString('utf8'); + + defaultContentType(event, 'image/svg+xml'); + setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000'); + + return svg; +}); diff --git a/docs/.vitepress/api/gh-icon/dpi/[...data].get.ts b/docs/.vitepress/api/gh-icon/dpi/[...data].get.ts index a09fcb2af0..2e5d4ebe40 100644 --- a/docs/.vitepress/api/gh-icon/dpi/[...data].get.ts +++ b/docs/.vitepress/api/gh-icon/dpi/[...data].get.ts @@ -28,7 +28,7 @@ export default eventHandler(async (event) => { stroke-width="2" stroke-linecap="round" stroke-linejoin="round" -` +`, ); const resvg = new Resvg(svg, { background: '#000' }); 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 1354b1ae47..db029f643f 100644 --- a/docs/.vitepress/api/gh-icon/stroke-width/[...data].get.ts +++ b/docs/.vitepress/api/gh-icon/stroke-width/[...data].get.ts @@ -1,12 +1,12 @@ -import { eventHandler, setResponseHeader, defaultContentType } from 'h3' -import { renderToString } from 'react-dom/server' -import { createElement } from 'react' +import { eventHandler, setResponseHeader, defaultContentType } from 'h3'; +import { renderToString } from 'react-dom/server'; +import { createElement } from 'react'; import SvgPreview from '../../../lib/SvgPreview/index.tsx'; -import createLucideIcon, { IconNode } from 'lucide-react/src/createLucideIcon' +import createLucideIcon, { IconNode } from 'lucide-react/src/createLucideIcon'; import { parseSync } from 'svgson'; export default eventHandler((event) => { - const { params } = event.context + const { params } = event.context; const [strokeWidth, svgData] = params.data.split('/'); const data = svgData.slice(0, -4); @@ -16,8 +16,8 @@ export default eventHandler((event) => { const Icon = createLucideIcon( 'icon', parseSync(src.includes('${src}`).children.map( - ({ name, attributes }) => [name, attributes] - ) as IconNode + ({ name, attributes }) => [name, attributes], + ) as IconNode, ); const svg = Buffer.from( @@ -33,12 +33,12 @@ export default eventHandler((event) => { @media screen and (prefers-color-scheme: dark) { svg { stroke: #fff; fill: transparent !important; } } - ` - ) + `, + ), ).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/icon-nodes/index.get.ts b/docs/.vitepress/api/icon-nodes/index.get.ts index a8623661ca..8ab634c78b 100644 --- a/docs/.vitepress/api/icon-nodes/index.get.ts +++ b/docs/.vitepress/api/icon-nodes/index.get.ts @@ -1,30 +1,30 @@ -import { eventHandler, getQuery, setResponseHeader } from 'h3' -import iconNodes from '../../data/iconNodes' -import { IconNodeWithKeys } from '../../theme/types' +import { eventHandler, getQuery, setResponseHeader } from 'h3'; +import iconNodes from '../../data/iconNodes'; +import { IconNodeWithKeys } from '../../theme/types'; export default eventHandler((event) => { - const query = getQuery(event) + const query = getQuery(event); - const withUniqueKeys = query.withUniqueKeys === 'true' + const withUniqueKeys = query.withUniqueKeys === 'true'; - setResponseHeader(event, 'Cache-Control', 'public, max-age=86400') - setResponseHeader(event, 'Access-Control-Allow-Origin', '*') + setResponseHeader(event, 'Cache-Control', 'public, max-age=86400'); + setResponseHeader(event, 'Access-Control-Allow-Origin', '*'); if (withUniqueKeys) { - return iconNodes + return iconNodes; } return Object.entries(iconNodes).reduce((acc, [name, iconNode]) => { if (withUniqueKeys) { - return [name, iconNode] + return [name, iconNode]; } - const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs}]) => { - return [name, attrs] - }) + const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs }]) => { + return [name, attrs]; + }); - acc[name] = newIconNode + acc[name] = newIconNode; - return acc - }, {}) -}) + return acc; + }, {}); +}); diff --git a/docs/.vitepress/api/icons/[iconName].get.ts b/docs/.vitepress/api/icons/[iconName].get.ts index 50fa8107af..e2d2091bb2 100644 --- a/docs/.vitepress/api/icons/[iconName].get.ts +++ b/docs/.vitepress/api/icons/[iconName].get.ts @@ -1,29 +1,29 @@ -import { eventHandler, getQuery, setResponseHeader, createError } from 'h3' -import iconNodes from '../../data/iconNodes' -import createLucideIcon from 'lucide-react/src/createLucideIcon' -import { renderToString } from 'react-dom/server' -import { createElement } from 'react' +import { eventHandler, getQuery, setResponseHeader, createError } from 'h3'; +import iconNodes from '../../data/iconNodes'; +import createLucideIcon from 'lucide-react/src/createLucideIcon'; +import { renderToString } from 'react-dom/server'; +import { createElement } from 'react'; export default eventHandler((event) => { - const { params } = event.context + const { params } = event.context; - const iconNode = iconNodes[params.iconName] + const iconNode = iconNodes[params.iconName]; if (iconNode == null) { const error = createError({ statusCode: 404, message: `Icon "${params.iconName}" not found`, - }) + }); - return sendError(event, error) + return sendError(event, error); } - const width = getQuery(event).width || undefined - const height = getQuery(event).height || undefined - const color = getQuery(event).color || undefined - const strokeWidth = getQuery(event).strokeWidth || undefined + const width = getQuery(event).width || undefined; + const height = getQuery(event).height || undefined; + const color = getQuery(event).color || undefined; + const strokeWidth = getQuery(event).strokeWidth || undefined; - const LucideIcon = createLucideIcon(params.iconName, iconNode) + const LucideIcon = createLucideIcon(params.iconName, iconNode); const svg = Buffer.from( renderToString( @@ -32,14 +32,13 @@ export default eventHandler((event) => { height, color: color ? `#${color}` : undefined, strokeWidth, - } - )) + }), + ), ).toString('utf8'); - defaultContentType(event, 'image/svg+xml') - setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000') - setResponseHeader(event, 'Access-Control-Allow-Origin', '*') + defaultContentType(event, 'image/svg+xml'); + setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000'); + setResponseHeader(event, 'Access-Control-Allow-Origin', '*'); - return svg - -}) + return svg; +}); diff --git a/docs/.vitepress/api/tags/index.get.ts b/docs/.vitepress/api/tags/index.get.ts index ab3c94ca5f..2ffccbdc02 100644 --- a/docs/.vitepress/api/tags/index.get.ts +++ b/docs/.vitepress/api/tags/index.get.ts @@ -1,11 +1,8 @@ -import { eventHandler, setResponseHeader } from 'h3' -import iconMetaData from '../../data/iconMetaData' +import iconMetaData from '../../data/iconMetaData'; export default eventHandler((event) => { - setResponseHeader(event, 'Cache-Control', 'public, max-age=86400') - setResponseHeader(event, 'Access-Control-Allow-Origin', '*') + setResponseHeader(event, 'Cache-Control', 'public, max-age=86400'); + setResponseHeader(event, 'Access-Control-Allow-Origin', '*'); - return Object.fromEntries( - Object.entries(iconMetaData).map(([name, { tags }]) => [ name, tags ]) - ) -}) + return Object.fromEntries(Object.entries(iconMetaData).map(([name, { tags }]) => [name, tags])); +}); diff --git a/docs/.vitepress/api/test.ts b/docs/.vitepress/api/test.ts index ebd7b88012..a3f2b67f30 100644 --- a/docs/.vitepress/api/test.ts +++ b/docs/.vitepress/api/test.ts @@ -1,3 +1,3 @@ export default eventHandler(() => { - return { nitro: 'Is Awesome! asda' } -}) + return { nitro: 'Is Awesome! asda' }; +}); diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 173ed778b4..8f75fa0050 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,10 +1,10 @@ -import { fileURLToPath, URL } from 'node:url' -import { defineConfig } from 'vitepress' +import { fileURLToPath, URL } from 'node:url'; +import { defineConfig } from 'vitepress'; import sidebar from './sidebar'; -const title = "Lucide"; -const socialTitle = "Lucide Icons"; -const description = "Beautiful & consistent icon toolkit made by the community." +const title = 'Lucide'; +const socialTitle = 'Lucide Icons'; +const description = 'Beautiful & consistent icon toolkit made by the community.'; // https://vitepress.dev/reference/site-config export default defineConfig({ @@ -19,86 +19,135 @@ export default defineConfig({ { find: /^.*\/VPIconAlignLeft\.vue$/, replacement: fileURLToPath( - new URL('./theme/components/overrides/VPIconAlignLeft.vue', import.meta.url) - ) + new URL('./theme/components/overrides/VPIconAlignLeft.vue', import.meta.url), + ), }, { find: /^.*\/VPFooter\.vue$/, replacement: fileURLToPath( - new URL('./theme/components/overrides/VPFooter.vue', import.meta.url) - ) - } - ] + new URL('./theme/components/overrides/VPFooter.vue', import.meta.url), + ), + }, + { + find: '~/.vitepress', + replacement: fileURLToPath(new URL('./', import.meta.url)), + }, + ], }, }, head: [ - [ '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" - }], + [ + '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', + }, + ], ], themeConfig: { // https://vitepress.dev/reference/default-theme-config logo: { light: '/logo.light.svg', - dark: '/logo.dark.svg' + dark: '/logo.dark.svg', }, nav: [ { text: 'Icons', link: '/icons/' }, @@ -110,21 +159,21 @@ export default defineConfig({ sidebar, socialLinks: [ { icon: 'github', link: 'https://github.com/lucide-icons/lucide' }, - { icon: 'discord', link: 'https://discord.gg/EH6nSts' } + { icon: 'discord', link: 'https://discord.gg/EH6nSts' }, ], footer: { message: 'Released under the ISC License.', - copyright: `Copyright ยฉ ${new Date().getFullYear()} Lucide Contributors` + copyright: `Copyright ยฉ ${new Date().getFullYear()} Lucide Contributors`, }, editLink: { - pattern: 'https://github.com/lucide-icons/lucide/edit/main/docs/:path' + pattern: 'https://github.com/lucide-icons/lucide/edit/main/docs/:path', }, carbonAds: { code: 'CWYIC53U', - placement: 'lucidedev' - } + placement: 'lucidedev', + }, }, sitemap: { - hostname: 'https://lucide.dev/' - } -}) + hostname: 'https://lucide.dev/', + }, +}); diff --git a/docs/.vitepress/data/categoriesData.json b/docs/.vitepress/data/categoriesData.json new file mode 100644 index 0000000000..03712cc237 --- /dev/null +++ b/docs/.vitepress/data/categoriesData.json @@ -0,0 +1,186 @@ +[ + { + "name": "accessibility", + "title": "Accessibility" + }, + { + "name": "account", + "title": "Accounts & access" + }, + { + "name": "animals", + "title": "Animals" + }, + { + "name": "arrows", + "title": "Arrows" + }, + { + "name": "brands", + "title": "Brands" + }, + { + "name": "buildings", + "title": "Buildings" + }, + { + "name": "charts", + "title": "Charts" + }, + { + "name": "communication", + "title": "Communication" + }, + { + "name": "connectivity", + "title": "Connectivity" + }, + { + "name": "currency", + "title": "Currency" + }, + { + "name": "cursors", + "title": "Cursors" + }, + { + "name": "design", + "title": "Design" + }, + { + "name": "development", + "title": "Coding & development" + }, + { + "name": "devices", + "title": "Devices" + }, + { + "name": "emoji", + "title": "Emoji" + }, + { + "name": "files", + "title": "File icons" + }, + { + "name": "food-beverage", + "title": "Food & beverage" + }, + { + "name": "furniture", + "title": "Furniture" + }, + { + "name": "gaming", + "title": "Gaming" + }, + { + "name": "home", + "title": "Home" + }, + { + "name": "layout", + "title": "Layout" + }, + { + "name": "mail", + "title": "Mail" + }, + { + "name": "maps", + "title": "Maps" + }, + { + "name": "maths", + "title": "Maths" + }, + { + "name": "medical", + "title": "Medical" + }, + { + "name": "money", + "title": "Money" + }, + { + "name": "multimedia", + "title": "Multimedia" + }, + { + "name": "nature", + "title": "Nature" + }, + { + "name": "navigation", + "title": "Navigation" + }, + { + "name": "notifications", + "title": "Notifications" + }, + { + "name": "people", + "title": "People" + }, + { + "name": "photography", + "title": "Photography" + }, + { + "name": "science", + "title": "Science" + }, + { + "name": "seasons", + "title": "Seasons" + }, + { + "name": "security", + "title": "Security" + }, + { + "name": "shapes", + "title": "Shapes" + }, + { + "name": "shopping", + "title": "Shopping" + }, + { + "name": "social", + "title": "Social" + }, + { + "name": "sports", + "title": "Sports" + }, + { + "name": "sustainability", + "title": "Sustainability" + }, + { + "name": "text", + "title": "Text formatting" + }, + { + "name": "time", + "title": "Time & calendar" + }, + { + "name": "tools", + "title": "Tools" + }, + { + "name": "transportation", + "title": "Transportation" + }, + { + "name": "travel", + "title": "Travel" + }, + { + "name": "weather", + "title": "Weather" + } +] \ No newline at end of file diff --git a/docs/.vitepress/data/packageData.json b/docs/.vitepress/data/packageData.json index e15de0eeed..613ed60e79 100644 --- a/docs/.vitepress/data/packageData.json +++ b/docs/.vitepress/data/packageData.json @@ -3,87 +3,160 @@ "order": 0, "icon": "js", "shields": [ - { "alt": "npm", "src": "https://img.shields.io/npm/v/lucide", "href": "https://www.npmjs.com/package/lucide" }, - { "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide", "href": "https://www.npmjs.com/package/lucide" } + { + "alt": "npm", + "src": "https://img.shields.io/npm/v/lucide", + "href": "https://www.npmjs.com/package/lucide" + }, + { + "alt": "npm", + "src": "https://img.shields.io/npm/dw/lucide", + "href": "https://www.npmjs.com/package/lucide" + } ] }, "lucide-react": { "order": 1, "icon": "react", "shields": [ - { "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-react", "href": "https://www.npmjs.com/package/lucide-react" }, - { "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-react", "href": "https://www.npmjs.com/package/lucide-react" } + { + "alt": "npm", + "src": "https://img.shields.io/npm/v/lucide-react", + "href": "https://www.npmjs.com/package/lucide-react" + }, + { + "alt": "npm", + "src": "https://img.shields.io/npm/dw/lucide-react", + "href": "https://www.npmjs.com/package/lucide-react" + } ] }, "lucide-vue": { "order": 2, "icon": "vue", "shields": [ - { "alt": "npm", "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", "href": "https://www.npmjs.com/package/lucide-vue" } + { + "alt": "npm", + "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", + "href": "https://www.npmjs.com/package/lucide-vue" + } ] }, "lucide-vue-next": { "order": 3, "icon": "vue-next", "shields": [ - { "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-vue-next", "href": "https://www.npmjs.com/package/lucide-vue-next" }, - { "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-vue-next", "href": "https://www.npmjs.com/package/lucide-vue-next" } + { + "alt": "npm", + "src": "https://img.shields.io/npm/v/lucide-vue-next", + "href": "https://www.npmjs.com/package/lucide-vue-next" + }, + { + "alt": "npm", + "src": "https://img.shields.io/npm/dw/lucide-vue-next", + "href": "https://www.npmjs.com/package/lucide-vue-next" + } ] }, "lucide-svelte": { "order": 4, "icon": "svelte", "shields": [ - { "alt": "npm", "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" } + { + "alt": "npm", + "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" + } ] }, "lucide-solid": { "order": 4, "icon": "solid", "shields": [ - { "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-solid", "href": "https://www.npmjs.com/package/lucide-solid" }, - { "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-solid", "href": "https://www.npmjs.com/package/lucide-solid" } + { + "alt": "npm", + "src": "https://img.shields.io/npm/v/lucide-solid", + "href": "https://www.npmjs.com/package/lucide-solid" + }, + { + "alt": "npm", + "src": "https://img.shields.io/npm/dw/lucide-solid", + "href": "https://www.npmjs.com/package/lucide-solid" + } ] }, "lucide-preact": { "order": 5, "icon": "preact", "shields": [ - { "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-preact", "href": "https://www.npmjs.com/package/lucide-preact" }, - { "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-preact", "href": "https://www.npmjs.com/package/lucide-preact" } + { + "alt": "npm", + "src": "https://img.shields.io/npm/v/lucide-preact", + "href": "https://www.npmjs.com/package/lucide-preact" + }, + { + "alt": "npm", + "src": "https://img.shields.io/npm/dw/lucide-preact", + "href": "https://www.npmjs.com/package/lucide-preact" + } ] }, "lucide-react-native": { "order": 6, "icon": "react-native", "shields": [ - { "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-react-native", "href": "https://www.npmjs.com/package/lucide-react-native" }, - { "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-react-native", "href": "https://www.npmjs.com/package/lucide-react-native" } + { + "alt": "npm", + "src": "https://img.shields.io/npm/v/lucide-react-native", + "href": "https://www.npmjs.com/package/lucide-react-native" + }, + { + "alt": "npm", + "src": "https://img.shields.io/npm/dw/lucide-react-native", + "href": "https://www.npmjs.com/package/lucide-react-native" + } ] }, "lucide-angular": { "order": 7, "icon": "angular", "shields": [ - { "alt": "npm", "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" } + { + "alt": "npm", + "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" + } ] }, "lucide-static": { "order": 8, "icon": "svg", "shields": [ - { "alt": "npm", "src": "https://img.shields.io/npm/v/lucide-static", "href": "https://www.npmjs.com/package/lucide-static" }, - { "alt": "npm", "src": "https://img.shields.io/npm/dw/lucide-static", "href": "https://www.npmjs.com/package/lucide-static" } - ] - }, - "lucide-flutter": { - "order": 9, - "icon": "flutter", - "shields": [ - { "alt": "flutter", "src": "https://img.shields.io/pub/v/lucide_icons", "href": "https://img.shields.io/pub/v/lucide_icons" } + { + "alt": "npm", + "src": "https://img.shields.io/npm/v/lucide-static", + "href": "https://www.npmjs.com/package/lucide-static" + }, + { + "alt": "npm", + "src": "https://img.shields.io/npm/dw/lucide-static", + "href": "https://www.npmjs.com/package/lucide-static" + } ] } } diff --git a/docs/.vitepress/data/packageData.thirdParty.json b/docs/.vitepress/data/packageData.thirdParty.json index ba35181a55..f167085fe3 100644 --- a/docs/.vitepress/data/packageData.thirdParty.json +++ b/docs/.vitepress/data/packageData.thirdParty.json @@ -76,5 +76,24 @@ ], "source": "https://github.com/swisnl/nuxt-lucide-icons", "documentation": "https://github.com/swisnl/nuxt-lucide-icons/blob/main/README.md" + }, + { + "name": "lucide-lustre", + "description": "A library providing https://lucide.dev icons to lustre", + "icon": "/framework-logos/lustre.webp", + "shields": [ + { + "alt": "Latest Stable Version", + "src": "https://img.shields.io/hexpm/v/lucide_lustre", + "href": "https://hex.pm/packages/lucide_lustre" + }, + { + "alt": "Total Downloads", + "src": "https://img.shields.io/hexpm/dw/lucide_lustre", + "href": "https://hex.pm/packages/lucide_lustre" + } + ], + "source": "https://github.com/dinkelspiel/lucide_lustre", + "documentation": "https://github.com/dinkelspiel/lucide_lustre/blob/master/README.md" } ] diff --git a/docs/.vitepress/data/teamData.json b/docs/.vitepress/data/teamData.json new file mode 100644 index 0000000000..4f46f57f1a --- /dev/null +++ b/docs/.vitepress/data/teamData.json @@ -0,0 +1,48 @@ +[ + { + "name": "Eric Fennis", + "title": "Creator of Lucide & Software engineer @nedap", + "image": "https://github.com/ericfennis.png?size=192", + "sponsor": "https://github.com/sponsors/ericfennis", + "socialLinks": [ + { + "icon": "github", + "link": "https://github.com/ericfennis" + }, + { + "icon": "x", + "link": "https://x.com/ericfennis" + } + ] + }, + { + "name": "Karsa Rigรณ", + "title": "Maintainer of Lucide & Software engineer @sztaki", + "image": "https://github.com/karsa-mistmere.png?size=192", + "socialLinks": [ + { + "icon": "github", + "link": "https://github.com/karsa-mistmere" + }, + { + "icon": "linkedin", + "link": "https://www.linkedin.com/in/karsamistmere" + } + ] + }, + { + "name": "Jakob Guddas", + "title": "Maintainer of Lucide & Software engineer @LEGO", + "image": "https://github.com/jguddas.png?size=192", + "socialLinks": [ + { + "icon": "github", + "link": "https://github.com/jguddas" + }, + { + "icon": "linkedin", + "link": "https://www.linkedin.com/in/jguddas" + } + ] + } +] diff --git a/docs/.vitepress/lib/SvgPreview/Backdrop.tsx b/docs/.vitepress/lib/SvgPreview/Backdrop.tsx index be9362523d..2999f0acfe 100644 --- a/docs/.vitepress/lib/SvgPreview/Backdrop.tsx +++ b/docs/.vitepress/lib/SvgPreview/Backdrop.tsx @@ -3,55 +3,92 @@ import React from 'react'; interface BackdropProps { src: string; color?: string; + outline?: boolean; backdropString: string; } -const Backdrop = ({ src, color = 'red', backdropString }: BackdropProps): JSX.Element => { +const Backdrop = ({ + src, + color = 'red', + outline = true, + backdropString, +}: BackdropProps): JSX.Element => { + const id = React.useId(); return ( <> - - + + - - - + + + - - - - + + + - - + + + + {outline && ( + + )} - - ); }; diff --git a/docs/.vitepress/lib/SvgPreview/Diff.tsx b/docs/.vitepress/lib/SvgPreview/Diff.tsx new file mode 100644 index 0000000000..3fa6840a57 --- /dev/null +++ b/docs/.vitepress/lib/SvgPreview/Diff.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import Backdrop from './Backdrop.tsx'; +import { darkModeCss, Grid } from './index.tsx'; + +const SvgPreview = React.forwardRef< + SVGSVGElement, + { + oldSrc: string; + newSrc: string; + } & React.SVGProps +>(({ oldSrc, newSrc, children, ...props }, ref) => { + return ( + + + + + + + + ${newSrc}`} + color="#777" + /> + + + {children} + + ); +}); + +export default SvgPreview; diff --git a/docs/.vitepress/lib/SvgPreview/index.tsx b/docs/.vitepress/lib/SvgPreview/index.tsx index f14523e6e6..562020a8ec 100644 --- a/docs/.vitepress/lib/SvgPreview/index.tsx +++ b/docs/.vitepress/lib/SvgPreview/index.tsx @@ -2,7 +2,23 @@ import React from 'react'; import { PathProps, Path } from './types'; import { getPaths, assert } from './utils'; -const Grid = ({ +export 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, + .svg-preview-shadow-group { + stroke: #fff; + } + } +`; + +export const Grid = ({ radius, fill = '#fff', ...props @@ -10,7 +26,11 @@ const Grid = ({ strokeWidth: number; radius: number; } & PathProps<'stroke', 'strokeWidth'>) => ( - + [ + .map((_, i) => i) + .filter((i) => i % 3 !== 2) + .flatMap((i) => [ + `M${props.strokeWidth} ${i + 1}h${24 - props.strokeWidth * 2}`, + `M${i + 1} ${props.strokeWidth}v${24 - props.strokeWidth * 2}`, + ]) + .join('') + } + /> + i) + .filter((i) => i % 3 === 2) + .flatMap((i) => [ `M${props.strokeWidth} ${i + 1}h${24 - props.strokeWidth * 2}`, `M${i + 1} ${props.strokeWidth}v${24 - props.strokeWidth * 2}`, ]) @@ -44,15 +82,21 @@ const Shadow = ({ paths: Path[]; } & PathProps<'stroke' | 'strokeWidth' | 'strokeOpacity', 'd'>) => { const groupedPaths = Object.entries( - paths.reduce((groups, val) => { - const key = val.c.id; - groups[key] = [...(groups[key] || []), val]; - return groups; - }, {} as Record) + paths.reduce( + (groups, val) => { + const key = val.c.id; + groups[key] = [...(groups[key] || []), val]; + return groups; + }, + {} as Record, + ), ); return ( <> - + {groupedPaths.map(([id, paths]) => ( - + [ @@ -74,9 +126,16 @@ const Shadow = ({ ))} - + {paths.map(({ d, c: { id } }, i) => ( - + ))} ) => ( - + {paths.map(({ d, c }, i) => ( - + ))} ); @@ -138,7 +204,15 @@ const ControlPath = ({ key={i} maskUnits="userSpaceOnUse" > - + @@ -146,7 +220,10 @@ const ControlPath = ({ ); })} - + {controlPaths.map(({ d, showMarker }, i) => ( ))} - + - showMarker ? [`M${prev.x} ${prev.y}h.01`, `M${next.x} ${next.y}h.01`] : [] + showMarker ? [`M${prev.x} ${prev.y}h.01`, `M${next.x} ${next.y}h.01`] : [], ) .join('')} /> {controlPaths.map(({ d, prev, next, startMarker, endMarker }, i) => ( - {startMarker && } - {endMarker && } + {startMarker && ( + + )} + {endMarker && ( + + )} ))} @@ -182,15 +274,16 @@ const Radii = ({ any >) => { return ( - + {paths.map( ({ c, prev, next, circle }, i) => circle && ( - {c.name !== "circle" && ( - + {c.name !== 'circle' && ( + )} @@ -208,11 +301,7 @@ const Radii = ({ cy={circle.y} cx={circle.x} r={circle.r} - stroke={ - (Math.round(circle.r * 1000) / 1000) % 1 !== 0 - ? "red" - : undefined - } + stroke={(Math.round(circle.r * 1000) / 1000) % 1 !== 0 ? 'red' : undefined} /> ), @@ -228,15 +317,29 @@ const Handles = ({ 'strokeWidth' | 'stroke' | 'strokeDasharray' | 'strokeOpacity', any >) => { - console.log(paths); return ( - + {paths.map(({ c, prev, next, cp1, cp2 }) => ( <> {cp1 && } - {cp1 && } + {cp1 && ( + + )} {cp2 && } - {cp2 && } + {cp2 && ( + + )} ))} @@ -252,19 +355,6 @@ 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: 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, - .svg-preview-shadow-group { - stroke: #fff; - } -}`; return ( - {showGrid && } - - + {showGrid && ( + + )} + + - - + + {children} ); diff --git a/docs/.vitepress/lib/SvgPreview/types.ts b/docs/.vitepress/lib/SvgPreview/types.ts index b36df0ca63..8c1b8f4edc 100644 --- a/docs/.vitepress/lib/SvgPreview/types.ts +++ b/docs/.vitepress/lib/SvgPreview/types.ts @@ -16,7 +16,7 @@ export type Path = { export type PathProps< RequiredProps extends keyof SVGProps, - NeverProps extends keyof SVGProps + NeverProps extends keyof SVGProps, > = Required, RequiredProps>> & Omit< React.SVGProps, diff --git a/docs/.vitepress/lib/SvgPreview/utils.ts b/docs/.vitepress/lib/SvgPreview/utils.ts index 2a9974a910..9dfb1a6f7e 100644 --- a/docs/.vitepress/lib/SvgPreview/utils.ts +++ b/docs/.vitepress/lib/SvgPreview/utils.ts @@ -51,7 +51,7 @@ export const getCommands = (src: string) => getNodes(src) .map(convertToPathNode) .flatMap(({ d, name }, idx) => - new SVGPathData(d).toAbs().commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })) + new SVGPathData(d).toAbs().commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })), ); export const getPaths = (src: string) => { @@ -60,10 +60,10 @@ export const getPaths = (src: string) => { let prev: Point | undefined = undefined; let start: Point | undefined = undefined; const addPath = ( - c: typeof commands[number], + c: (typeof commands)[number], next: Point, d?: string, - extras?: { circle?: Path['circle']; cp1?: Path['cp1']; cp2?: Path['cp2'] } + extras?: { circle?: Path['circle']; cp1?: Path['cp1']; cp2?: Path['cp2'] }, ) => { assert(prev); paths.push({ @@ -153,7 +153,7 @@ export const getPaths = (src: string) => { { cp1: { x: prev.x - reflectedCp1.x, y: prev.y - reflectedCp1.y }, cp2: { x: c.x2, y: c.y2 }, - } + }, ); break; } @@ -169,7 +169,7 @@ export const getPaths = (src: string) => { assert(prev); const backTrackCP = ( index: number, - currentPoint: { x: number; y: number } + currentPoint: { x: number; y: number }, ): { x: number; y: number } => { const previousCommand = commands[index - 1]; if (!previousCommand) { @@ -211,7 +211,7 @@ export const getPaths = (src: string) => { { cp1: { x: prevCP.x, y: prevCP.y }, cp2: { x: prevCP.x, y: prevCP.y }, - } + }, ); break; } @@ -226,13 +226,13 @@ export const getPaths = (src: string) => { c.lArcFlag, c.sweepFlag, c.x, - c.y + c.y, ); addPath( c, c, `M ${prev.x} ${prev.y} A${c.rX} ${c.rY} ${c.xRot} ${c.lArcFlag} ${c.sweepFlag} ${c.x} ${c.y}`, - { circle: c.rX === c.rY ? { ...center, r: c.rX } : undefined } + { circle: c.rX === c.rY ? { ...center, r: c.rX } : undefined }, ); break; } @@ -253,7 +253,7 @@ export const arcEllipseCenter = ( fa: number, fs: number, x2: number, - y2: number + y2: number, ) => { const phi = (a * Math.PI) / 180; @@ -280,7 +280,7 @@ export const arcEllipseCenter = ( sign * Math.sqrt( Math.max(rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p, 0) / - (rx * rx * y1p * y1p + ry * ry * x1p * x1p) + (rx * rx * y1p * y1p + ry * ry * x1p * x1p), ); const V2 = [(rx * y1p) / ry, (-ry * x1p) / rx]; diff --git a/docs/.vitepress/lib/categories.ts b/docs/.vitepress/lib/categories.ts index 2e14bbe71e..dfc0268b63 100644 --- a/docs/.vitepress/lib/categories.ts +++ b/docs/.vitepress/lib/categories.ts @@ -1,28 +1,34 @@ -import fs from "fs"; -import path from "path"; -import {Category, IconEntity} from "../theme/types"; +import fs from 'fs'; +import path from 'path'; +import { Category, IconEntity } from '../theme/types'; -const directory = path.join(process.cwd(), "../categories"); +const directory = path.join(process.cwd(), '../categories'); export function getAllCategoryFiles(): Category[] { const fileNames = fs.readdirSync(directory).filter((file) => path.extname(file) === '.json'); return fileNames.map((fileName) => { - const name = path.basename(fileName, '.json') - const fileContent = fs.readFileSync(path.join(directory, fileName), 'utf8') + const name = path.basename(fileName, '.json'); + const fileContent = fs.readFileSync(path.join(directory, fileName), 'utf8'); - const parsedFileContent = JSON.parse(fileContent) + const parsedFileContent = JSON.parse(fileContent); return { name, title: parsedFileContent.title, - } + }; }); } -export function mapCategoryIconCount(categories: Category[], icons: { categories: IconEntity['categories'] }[]) { +export function mapCategoryIconCount( + categories: Category[], + icons: { categories: IconEntity['categories'] }[], +) { return categories.map((category) => ({ ...category, - iconCount: icons.reduce((acc, curr) => (curr.categories.includes(category.name) ? ++acc : acc), 0) - })) + iconCount: icons.reduce( + (acc, curr) => (curr.categories.includes(category.name) ? ++acc : acc), + 0, + ), + })); } diff --git a/docs/.vitepress/lib/createCodeExamples.ts b/docs/.vitepress/lib/codeExamples/createCodeExamples.ts similarity index 58% rename from docs/.vitepress/lib/createCodeExamples.ts rename to docs/.vitepress/lib/codeExamples/createCodeExamples.ts index 41c75551c5..7848841c2e 100644 --- a/docs/.vitepress/lib/createCodeExamples.ts +++ b/docs/.vitepress/lib/codeExamples/createCodeExamples.ts @@ -1,33 +1,33 @@ -import { - bundledLanguages, - type ThemeRegistration -} from 'shikiji' -import { - getHighlighter, -} from 'shikiji' - +import { bundledLanguages, type ThemeRegistration } from 'shikiji'; +import { getHighlighter } from 'shikiji'; type CodeExampleType = { - title: string, - language: string, - code: string, -}[] + title: string; + language: string; + code: string; +}[]; const getIconCodes = (): CodeExampleType => { return [ { - language: 'html', - title: 'HTML', - code: `` + language: 'js', + title: 'Vanilla', + code: `\ +import { createIcons, icons } from 'lucide'; + +createIcons({ icons }); + +document.body.append('');\ + `, }, { language: 'tsx', title: 'React', - code: `import { PascalCase } from 'lucide-react'; + code: `import { $PascalCase } from 'lucide-react'; const App = () => { return ( - + <$PascalCase /> ); }; @@ -38,11 +38,11 @@ export default App; language: 'vue', title: 'Vue', code: ` `, }, @@ -50,20 +50,20 @@ export default App; language: 'svelte', title: 'Svelte', code: ` - +<$PascalCase /> `, }, { language: 'tsx', title: 'Preact', - code: `import { PascalCase } from 'lucide-preact'; + code: `import { $PascalCase } from 'lucide-preact'; const App = () => { return ( - + <$PascalCase /> ); }; @@ -73,11 +73,11 @@ export default App; { language: 'tsx', title: 'Solid', - code: `import { PascalCase } from 'lucide-solid'; + code: `import { $PascalCase } from 'lucide-solid'; const App = () => { return ( - + <$PascalCase /> ); }; @@ -88,16 +88,16 @@ export default App; language: 'tsx', title: 'Angular', code: `// app.module.ts -import { LucideAngularModule, PascalCase } from 'lucide-angular'; +import { LucideAngularModule, $PascalCase } from 'lucide-angular'; @NgModule({ imports: [ - LucideAngularModule.pick({ PascalCase }) + LucideAngularModule.pick({ $PascalCase }) ], }) // app.component.html - + `, }, { @@ -107,38 +107,39 @@ import { LucideAngularModule, PascalCase } from 'lucide-angular'; @import ('~lucide-static/font/Lucide.css'); -

+
`, - } - ] -} + }, + ]; +}; export type ThemeOptions = | ThemeRegistration - | { light: ThemeRegistration; dark: ThemeRegistration } + | { light: ThemeRegistration; dark: ThemeRegistration }; const highLightCode = async (code: string, lang: string, active?: boolean) => { const highlighter = await getHighlighter({ themes: ['github-light', 'github-dark'], - langs: Object.keys(bundledLanguages) - }) - - const highlightedCode = highlighter.codeToHtml(code, { - lang, - themes: { - light: 'github-light', - dark: 'github-dark' - }, - defaultColor: false - }).replace('shiki-themes', 'shiki-themes vp-code') + langs: Object.keys(bundledLanguages), + }); + + const highlightedCode = highlighter + .codeToHtml(code, { + lang, + themes: { + light: 'github-light', + dark: 'github-dark', + }, + defaultColor: false, + }) + .replace('shiki-themes', 'shiki-themes vp-code'); return `
${lang} ${highlightedCode} -
` -} - + `; +}; export default async function createCodeExamples() { const codes = getIconCodes(); @@ -153,7 +154,7 @@ export default async function createCodeExamples() { language: language, code: codeString, }; - }) + }); return Promise.all(codeExamplePromises); } diff --git a/docs/.vitepress/lib/codeExamples/createLabCodeExamples.ts b/docs/.vitepress/lib/codeExamples/createLabCodeExamples.ts new file mode 100644 index 0000000000..4de2245e8b --- /dev/null +++ b/docs/.vitepress/lib/codeExamples/createLabCodeExamples.ts @@ -0,0 +1,161 @@ +import { bundledLanguages, type ThemeRegistration } from 'shikiji'; +import { getHighlighter } from 'shikiji'; + +type CodeExampleType = { + title: string; + language: string; + code: string; +}[]; + +const getIconCodes = (): CodeExampleType => { + return [ + { + language: 'js', + title: 'Vanilla', + code: `\ +import { createIcons, icons } from 'lucide'; +import { $CamelCase } from '@lucide/lab'; + +createIcons({ + icons: { + $CamelCase + } +}); + +document.body.append('');\ + `, + }, + { + language: 'tsx', + title: 'React', + code: `import { Icon } from 'lucide-react'; +import { $CamelCase } from '@lucide/lab'; + +const App = () => { + return ( + + ); +}; + +export default App; +`, + }, + { + language: 'vue', + title: 'Vue', + code: ` + + +`, + }, + { + language: 'svelte', + title: 'Svelte', + code: ` + + +`, + }, + { + language: 'tsx', + title: 'Preact', + code: `import { Icon } from 'lucide-preact'; +import { $CamelCase } from '@lucide/lab'; + +const App = () => { + return ( + + ); +}; + +export default App; +`, + }, + { + language: 'tsx', + title: 'Solid', + code: `import { Icon } from 'lucide-solid'; +import { $CamelCase } from '@lucide/lab'; + +const App = () => { + return ( + + ); +}; + +export default App; +`, + }, + { + language: 'tsx', + title: 'Angular', + code: `// app.module.ts +import { LucideAngularModule } from 'lucide-angular'; +import { $CamelCase } from '@lucide/lab'; + +@NgModule({ + imports: [ + LucideAngularModule.pick({ $CamelCase }) + ], +}) + +// app.component.html + +`, + }, + ]; +}; + +export type ThemeOptions = + | ThemeRegistration + | { light: ThemeRegistration; dark: ThemeRegistration }; + +const highLightCode = async (code: string, lang: string, active?: boolean) => { + const highlighter = await getHighlighter({ + themes: ['github-light', 'github-dark'], + langs: Object.keys(bundledLanguages), + }); + + const highlightedCode = highlighter + .codeToHtml(code, { + lang, + themes: { + light: 'github-light', + dark: 'github-dark', + }, + defaultColor: false, + }) + .replace('shiki-themes', 'shiki-themes vp-code'); + + return `
+ + ${lang} + ${highlightedCode} +
`; +}; + +export default async function createCodeExamples() { + const codes = getIconCodes(); + + const codeExamplePromises = codes.map(async ({ title, language, code }, index) => { + const isFirst = index === 0; + + const codeString = await highLightCode(code, language, isFirst); + + return { + title, + language: language, + code: codeString, + }; + }); + + return Promise.all(codeExamplePromises); +} diff --git a/docs/.vitepress/lib/codeExamples/highLightCode.ts b/docs/.vitepress/lib/codeExamples/highLightCode.ts new file mode 100644 index 0000000000..ed51ce21bc --- /dev/null +++ b/docs/.vitepress/lib/codeExamples/highLightCode.ts @@ -0,0 +1,32 @@ +import { bundledLanguages, type ThemeRegistration } from 'shikiji'; +import { getHighlighter } from 'shikiji'; + +export type ThemeOptions = + | ThemeRegistration + | { light: ThemeRegistration; dark: ThemeRegistration }; + +const highLightCode = async (code: string, lang: string, active?: boolean) => { + const highlighter = await getHighlighter({ + themes: ['github-light', 'github-dark'], + langs: Object.keys(bundledLanguages), + }); + + const highlightedCode = highlighter + .codeToHtml(code, { + lang, + themes: { + light: 'github-light', + dark: 'github-dark', + }, + defaultColor: false, + }) + .replace('shiki-themes', 'shiki-themes vp-code'); + + return `
+ + ${lang} + ${highlightedCode} +
`; +}; + +export default highLightCode; diff --git a/docs/.vitepress/lib/codeExamples/types.ts b/docs/.vitepress/lib/codeExamples/types.ts new file mode 100644 index 0000000000..1ab47b4696 --- /dev/null +++ b/docs/.vitepress/lib/codeExamples/types.ts @@ -0,0 +1,5 @@ +export type CodeExampleType = { + title: string; + language: string; + code: string; +}[]; diff --git a/docs/.vitepress/lib/fetchPackages.ts b/docs/.vitepress/lib/fetchPackages.ts index 0bf1a7e650..aede278396 100644 --- a/docs/.vitepress/lib/fetchPackages.ts +++ b/docs/.vitepress/lib/fetchPackages.ts @@ -1,38 +1,36 @@ import { promises as fs, constants } from 'fs'; import path from 'path'; -import yaml from 'js-yaml' import { PackageItem } from '../theme/types'; -const fileExist = (filePath) => fs.access(filePath, constants.F_OK).then(() => true).catch(() => false) +const fileExist = (filePath) => + fs + .access(filePath, constants.F_OK) + .then(() => true) + .catch(() => false); const fetchPackages = async (): Promise => { const docsDir = path.resolve(process.cwd(), '../packages'); - const fileNames = await (await fs.readdir(docsDir)).map(filename => ({filename, directory: docsDir})) + const fileNames = await ( + await fs.readdir(docsDir) + ).map((filename) => ({ filename, directory: docsDir })); - const packageJsons = await Promise.all(fileNames.map( async ({filename, directory}) => { - const filePath = path.resolve(directory, filename) - const fileStat = await fs.lstat(filePath); + const packageJsons = await Promise.all( + fileNames.map(async ({ filename, directory }) => { + const filePath = path.resolve(directory, filename); + const fileStat = await fs.lstat(filePath); - if(!fileStat.isDirectory()) return null; + if (!fileStat.isDirectory()) return null; - const jsonFilePath = path.resolve(filePath, 'package.json') - if (await fileExist(jsonFilePath)) { - return JSON.parse( - await fs.readFile(jsonFilePath, 'utf-8') - ) - } + const jsonFilePath = path.resolve(filePath, 'package.json'); + if (await fileExist(jsonFilePath)) { + return JSON.parse(await fs.readFile(jsonFilePath, 'utf-8')); + } - const ymlFilePath = path.resolve(filePath, 'pubspec.yaml') - if(await fileExist(ymlFilePath)) { - return yaml.load( - await fs.readFile(ymlFilePath, 'utf-8') - ); - } + return null; + }), + ); - return null - })) - - return packageJsons -} + return packageJsons; +}; export default fetchPackages; diff --git a/docs/.vitepress/lib/generateZip.ts b/docs/.vitepress/lib/generateZip.ts index e647eab636..7b95011fd9 100644 --- a/docs/.vitepress/lib/generateZip.ts +++ b/docs/.vitepress/lib/generateZip.ts @@ -1,17 +1,15 @@ -export type IconContent = [icon: string, src:string]; +export type IconContent = [icon: string, src: string]; async function generateZip(icons: IconContent[]) { - const JSZip = (await import('jszip')).default + const JSZip = (await import('jszip')).default; const zip = new JSZip(); - const addingZipPromises = icons.map(([name, src]) => - zip.file(`${name}.svg`, src), - ); + const addingZipPromises = icons.map(([name, src]) => zip.file(`${name}.svg`, src)); - await Promise.all(addingZipPromises) + await Promise.all(addingZipPromises); return zip.generateAsync({ type: 'blob' }); } -export default generateZip +export default generateZip; diff --git a/docs/.vitepress/lib/getFallbackZip.tsx b/docs/.vitepress/lib/getFallbackZip.tsx index 82f3bbdaf8..9760697e9f 100644 --- a/docs/.vitepress/lib/getFallbackZip.tsx +++ b/docs/.vitepress/lib/getFallbackZip.tsx @@ -1,17 +1,15 @@ -import { createLucideIcon } from "lucide-react/src/lucide-react" -import { type LucideProps, type IconNode } from "lucide-react/src/createLucideIcon" -import { IconEntity } from "../theme/types" +import { createLucideIcon } from 'lucide-react/src/lucide-react'; +import { type LucideProps, type IconNode } from 'lucide-react/src/createLucideIcon'; +import { IconEntity } from '../theme/types'; import { renderToStaticMarkup } from 'react-dom/server'; -import { IconContent } from "./generateZip"; +import { IconContent } from './generateZip'; const getFallbackZip = (icons: IconEntity[], params: LucideProps) => { - return icons - .map((icon) => { - const Icon = createLucideIcon(icon.name, icon.iconNode as IconNode) - const src = renderToStaticMarkup() - return [icon.name, src] - }) -} + return icons.map((icon) => { + const Icon = createLucideIcon(icon.name, icon.iconNode as IconNode); + const src = renderToStaticMarkup(); + return [icon.name, src]; + }); +}; - -export default getFallbackZip +export default getFallbackZip; diff --git a/docs/.vitepress/lib/icons.ts b/docs/.vitepress/lib/icons.ts index ac1c6c94c8..ece3f9bfd3 100644 --- a/docs/.vitepress/lib/icons.ts +++ b/docs/.vitepress/lib/icons.ts @@ -1,34 +1,34 @@ -import fs from "fs"; -import path from "path"; -import { IconNodeWithKeys } from "../theme/types"; -import iconNodes from '../data/iconNodes' -import releaseMeta from "../data/releaseMetaData.json"; +import fs from 'fs'; +import path from 'path'; +import { IconNodeWithKeys } from '../theme/types'; +import iconNodes from '../data/iconNodes'; +import releaseMeta from '../data/releaseMetaData.json'; const DATE_OF_FORK = '2020-06-08T16:39:52+0100'; -const directory = path.join(process.cwd(), "../icons"); +const directory = path.join(process.cwd(), '../icons'); export interface GetDataOptions { - withChildKeys?: boolean + withChildKeys?: boolean; } export async function getData(name: string) { const jsonPath = path.join(directory, `${name}.json`); - const jsonContent = fs.readFileSync(jsonPath, "utf8"); + const jsonContent = fs.readFileSync(jsonPath, 'utf8'); const { tags, categories, contributors } = JSON.parse(jsonContent); - const iconNode = iconNodes[name] + const iconNode = iconNodes[name]; const releaseData = releaseMeta?.[name] ?? { - "createdRelease": { - "version": "0.0.0", - "date": DATE_OF_FORK + createdRelease: { + version: '0.0.0', + date: DATE_OF_FORK, }, - "changedRelease": { - "version": "0.0.0", - "date": DATE_OF_FORK - } - } + changedRelease: { + version: '0.0.0', + date: DATE_OF_FORK, + }, + }; return { name, @@ -36,11 +36,11 @@ export async function getData(name: string) { categories, iconNode, contributors, - ...releaseData + ...releaseData, }; } -export async function getAllData(): Promise<{ name: string, iconNode: IconNodeWithKeys}[]> { +export async function getAllData(): Promise<{ name: string; iconNode: IconNodeWithKeys }[]> { const names = Object.keys(iconNodes); return Promise.all(names.map((name) => getData(name))); diff --git a/docs/.vitepress/sidebar.ts b/docs/.vitepress/sidebar.ts index 762427aa94..5833648f07 100644 --- a/docs/.vitepress/sidebar.ts +++ b/docs/.vitepress/sidebar.ts @@ -1,50 +1,54 @@ -import { DefaultTheme, UserConfig } from "vitepress" +import { DefaultTheme, UserConfig } from 'vitepress'; const sidebar: UserConfig['themeConfig']['sidebar'] = { - 'guide':[ + guide: [ { text: 'Introduction', items: [ { text: 'What is lucide?', link: '/guide/' }, { text: 'Installation', link: '/guide/installation' }, - { text: 'Comparison', link: '/guide/comparison' } - ] + { text: 'Comparison', link: '/guide/comparison' }, + ], }, { text: 'Basics', items: [ { text: 'Color', - link: '/guide/basics/color' + link: '/guide/basics/color', }, { text: 'Sizing', - link: '/guide/basics/sizing' + link: '/guide/basics/sizing', }, { text: 'Stroke width', - link: '/guide/basics/stroke-width' + link: '/guide/basics/stroke-width', }, - ] + ], }, // TODO: Add this section { text: 'Advanced', items: [ - // { - // text: 'Accessibility', - // link: '/guide/advanced/accessibility' - // }, + { + text: 'Accessibility', + link: '/guide/advanced/accessibility', + }, { text: 'Global styling', - link: '/guide/advanced/global-styling' + link: '/guide/advanced/global-styling', }, // { // text: 'Animations', // }, { text: 'Filled icons', - link: '/guide/advanced/filled-icons' + link: '/guide/advanced/filled-icons', + }, + { + text: 'Aliased Names', + link: '/guide/advanced/aliased-names', }, // { // text: 'Combining icons', @@ -55,75 +59,77 @@ const sidebar: UserConfig['themeConfig']['sidebar'] = { // { // text: 'Auto importing' // }, - ] + ], }, { text: 'Packages', items: [ { text: 'Lucide', - link: '/guide/packages/lucide' + link: '/guide/packages/lucide', }, { text: 'Lucide React', - link: '/guide/packages/lucide-react' + link: '/guide/packages/lucide-react', }, { text: 'Lucide React Native', - link: '/guide/packages/lucide-react-native' + link: '/guide/packages/lucide-react-native', }, { text: 'Lucide Vue', - link: '/guide/packages/lucide-vue-next' + link: '/guide/packages/lucide-vue-next', }, { text: 'Lucide Svelte', - link: '/guide/packages/lucide-svelte' + link: '/guide/packages/lucide-svelte', }, { text: 'Lucide Solid', - link: '/guide/packages/lucide-solid' + link: '/guide/packages/lucide-solid', }, { text: 'Lucide Preact', - link: '/guide/packages/lucide-preact' + link: '/guide/packages/lucide-preact', }, { text: 'Lucide Angular', - link: '/guide/packages/lucide-angular' + link: '/guide/packages/lucide-angular', }, { text: 'Lucide Static', - link: '/guide/packages/lucide-static' + link: '/guide/packages/lucide-static', }, - ] + ], }, { text: 'Contributing', items: [ { text: 'Icon Design Principles', - link: '/guide/design/icon-design-guide' + link: '/guide/design/icon-design-guide', }, { text: 'Designing in Illustrator', - link: '/guide/design/illustrator-guide' + link: '/guide/design/illustrator-guide', }, { text: 'Designing in InkScape', - link: '/guide/design/inkscape-guide' + link: '/guide/design/inkscape-guide', }, { text: 'Designing in Figma', - link: '/guide/design/figma-guide' + link: '/guide/design/figma-guide', + }, + { + text: 'Designing in Affinity Designer', + link: '/guide/design/affinity-designer-guide', }, - ] + ], }, ], // This should be here to keep the sidebar shown on the icons page - 'icons': [ - { text: '', link: '/' }, - ], -} + icons: [{ text: '', link: '/' }], +}; -export default sidebar +export default sidebar; diff --git a/docs/.vitepress/theme/components/base/Badge.vue b/docs/.vitepress/theme/components/base/Badge.vue index aabe58c569..7926c64836 100644 --- a/docs/.vitepress/theme/components/base/Badge.vue +++ b/docs/.vitepress/theme/components/base/Badge.vue @@ -1,6 +1,5 @@ diff --git a/docs/.vitepress/theme/components/home/HomeHeroIconsCard.data.ts b/docs/.vitepress/theme/components/home/HomeHeroIconsCard.data.ts index b6898c80f5..e8c158c5ca 100644 --- a/docs/.vitepress/theme/components/home/HomeHeroIconsCard.data.ts +++ b/docs/.vitepress/theme/components/home/HomeHeroIconsCard.data.ts @@ -1,16 +1,17 @@ -import iconNodes from '../../../data/iconNodes' +import iconNodes from '../../../data/iconNodes'; -const getRandomItem = (items: Item[]): Item => items[Math.floor(Math.random()*items.length)]; +const getRandomItem = (items: Item[]): Item => + items[Math.floor(Math.random() * items.length)]; export default { async load() { - const icons = Object.entries(iconNodes).map(([name, iconNode]) => ({ name, iconNode })) + const icons = Object.entries(iconNodes).map(([name, iconNode]) => ({ name, iconNode })); - const randomIcons = Array.from({ length: 200 }, () => getRandomItem(icons)) + const randomIcons = Array.from({ length: 200 }, () => getRandomItem(icons)); return { icons: randomIcons, iconsCount: icons.length, - } - } -} + }; + }, +}; diff --git a/docs/.vitepress/theme/components/home/HomeHeroIconsCard.vue b/docs/.vitepress/theme/components/home/HomeHeroIconsCard.vue index 7331c77064..9471e815f5 100644 --- a/docs/.vitepress/theme/components/home/HomeHeroIconsCard.vue +++ b/docs/.vitepress/theme/components/home/HomeHeroIconsCard.vue @@ -28,8 +28,6 @@ function insert() { const replaceIndex = random(0, 48) const newIcon = getRandomNewIcon() - // items.value.splice(replaceIndex, 0, newIcon); - items.value[replaceIndex] = newIcon } @@ -76,7 +74,6 @@ onBeforeUnmount(() => { diff --git a/docs/.vitepress/theme/components/home/HomeSponsorCard.vue b/docs/.vitepress/theme/components/home/HomeSponsorCard.vue new file mode 100644 index 0000000000..9a680b4111 --- /dev/null +++ b/docs/.vitepress/theme/components/home/HomeSponsorCard.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/docs/.vitepress/theme/components/home/HomeTeamSection.vue b/docs/.vitepress/theme/components/home/HomeTeamSection.vue new file mode 100644 index 0000000000..e128ca6d45 --- /dev/null +++ b/docs/.vitepress/theme/components/home/HomeTeamSection.vue @@ -0,0 +1,91 @@ + + + + + + diff --git a/docs/.vitepress/theme/components/home/TeamMemberCard.vue b/docs/.vitepress/theme/components/home/TeamMemberCard.vue new file mode 100644 index 0000000000..59c900b61b --- /dev/null +++ b/docs/.vitepress/theme/components/home/TeamMemberCard.vue @@ -0,0 +1,92 @@ + + + + + + + diff --git a/docs/.vitepress/theme/components/icons/CarbonAdOverlay.vue b/docs/.vitepress/theme/components/icons/CarbonAdOverlay.vue new file mode 100644 index 0000000000..548e15b203 --- /dev/null +++ b/docs/.vitepress/theme/components/icons/CarbonAdOverlay.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/docs/.vitepress/theme/components/icons/CategoryList.data.ts b/docs/.vitepress/theme/components/icons/CategoryList.data.ts index bb32e80385..18224cbee5 100644 --- a/docs/.vitepress/theme/components/icons/CategoryList.data.ts +++ b/docs/.vitepress/theme/components/icons/CategoryList.data.ts @@ -1,16 +1,15 @@ import { getAllData } from '../../../lib/icons'; import { getAllCategoryFiles, mapCategoryIconCount } from '../../../lib/categories'; -import iconsMetaData from '../../../data/iconMetaData' - +import iconsMetaData from '../../../data/iconMetaData'; export default { async load() { - let categories = getAllCategoryFiles() + let categories = getAllCategoryFiles(); - categories = mapCategoryIconCount(categories, Object.values(iconsMetaData)) + categories = mapCategoryIconCount(categories, Object.values(iconsMetaData)); return { categories, - } - } -} + }; + }, +}; diff --git a/docs/.vitepress/theme/components/icons/CategoryList.vue b/docs/.vitepress/theme/components/icons/CategoryList.vue index 6e645ab5e2..49452e2709 100644 --- a/docs/.vitepress/theme/components/icons/CategoryList.vue +++ b/docs/.vitepress/theme/components/icons/CategoryList.vue @@ -1,13 +1,16 @@ diff --git a/docs/.vitepress/theme/components/icons/CopyCodeButton.vue b/docs/.vitepress/theme/components/icons/CopyCodeButton.vue index 13b1624efa..a58fcf4f59 100644 --- a/docs/.vitepress/theme/components/icons/CopyCodeButton.vue +++ b/docs/.vitepress/theme/components/icons/CopyCodeButton.vue @@ -1,92 +1,98 @@ @@ -100,10 +106,11 @@ function copyAngular() { data-confetti-text="Copied!" :popoverPosition="popoverPosition" :options="[ - { text: 'Copy JSX' , onClick: copyJSX }, - { text: 'Copy Vue' , onClick: copyVue }, - { text: 'Copy Svelte' , onClick: copyJSX }, - { text: 'Copy Angular' , onClick: copyAngular }, + { text: 'Copy JSX', onClick: copyJSX }, + { text: 'Copy Component Name', onClick: copyComponentName }, + { text: 'Copy Vue', onClick: copyVue }, + { text: 'Copy Svelte', onClick: copyJSX }, + { text: 'Copy Angular', onClick: copyAngular }, ]" /> diff --git a/docs/.vitepress/theme/components/icons/IconDetailOverlay.vue b/docs/.vitepress/theme/components/icons/IconDetailOverlay.vue index ec51509534..43f8f7fee1 100644 --- a/docs/.vitepress/theme/components/icons/IconDetailOverlay.vue +++ b/docs/.vitepress/theme/components/icons/IconDetailOverlay.vue @@ -11,14 +11,31 @@ import IconInfo from './IconInfo.vue'; import Badge from '../base/Badge.vue'; import { computedAsync } from '@vueuse/core'; import { satisfies } from 'semver'; +import { useExternalLibs } from '../../composables/useExternalLibs'; const props = defineProps<{ - iconName: string + iconName: string | null }>() +const { externalIconNodes } = useExternalLibs() + +const { go } = useRouter() + const icon = computedAsync(async () => { if (props.iconName) { - return (await import(`../../../data/iconDetails/${props.iconName}.ts`)).default as IconEntity + try { + if (props.iconName.includes(':')) { + const [library, name] = props.iconName.split(':') + + return externalIconNodes.value[library].find((icon) => icon.name === name) + } else { + return (await import(`../../../data/iconDetails/${props.iconName}.ts`)).default as IconEntity + } + } catch (err) { + if (!props.iconName.includes(':')) { + go(`/icons/${props.iconName}`) + } + } } return null }, null) @@ -36,8 +53,6 @@ function onClose() { emit('close') } -const { go } = useRouter() - const CloseIcon = createLucideIcon('Close', x) const Expand = createLucideIcon('Expand', expand) @@ -51,10 +66,8 @@ const Expand = createLucideIcon('Expand', expand) v-if="icon.createdRelease" class="version" :href="releaseTagLink(icon.createdRelease.version)" - target="_blank" - rel="noreferrer noopener" >v{{ icon.createdRelease.version }} - + @@ -144,11 +157,11 @@ const Expand = createLucideIcon('Expand', expand) } .drawer-enter-active { - transition: all 0.2s cubic-bezier(.21,.8,.46,.9); + transition: opacity 0.5s, transform 0.25s ease; } .drawer-leave-active { - transition: all 0.4s cubic-bezier(1, 0.5, 0.8, 1); + transition: opacity 0.25s ease, transform 1.6s ease-out; } .drawer-enter-from, diff --git a/docs/.vitepress/theme/components/icons/IconGrid.vue b/docs/.vitepress/theme/components/icons/IconGrid.vue index 6d109e256f..1919eb6dd2 100644 --- a/docs/.vitepress/theme/components/icons/IconGrid.vue +++ b/docs/.vitepress/theme/components/icons/IconGrid.vue @@ -4,7 +4,7 @@ import IconItem from './IconItem.vue' const emit = defineEmits(['setActiveIcon']) -const props = defineProps<{ +defineProps<{ icons: IconEntity[] activeIcon?: string overlayMode?: boolean @@ -25,8 +25,10 @@ function setActiveIcon(name: string) { :key="icon.name" > { if (!props.icon || !props?.icon?.tags) return [] return props.icon.tags.join(' โ€ข ') }) + +const DiamondIcon = createLucideIcon('Diamond', diamond)