- Implemented `createIconIndex` to resolve icon markers and manage metadata.
- Introduced `metadata.mts` for defining metadata fields and types.
- Created `model.mts` for handling metadata suggestions and OpenAI interactions.
- Developed `pullRequest.mts` to manage pull request suggestions and caching.
- Added `referenceExamples.mts` for generating reference examples based on icon metadata.
- Established `repository.mts` for loading categories, groups, and icons from the repository.
- Built `suggestionComments.mts` to generate GitHub review comments with suggestions for icon metadata.
* feat(metadata): reusable tag groups with `$group:`
A `$group:<group-name>` marker in `tags` is replaced in place with the tags of
`groups/<group-name>.json`: a small set of tags for what a variant suffix
means, such as `-minus` → remove, delete.
- Read groups in `readGroups` and resolve markers next to `$extends:`,
failing on unknown groups and on `$group:` outside `tags`
- Add `group.schema.json` and lint, spell-check and format `groups/*.json`
- `pnpm checkIcons` also fails on literals a group already provides, and
warns about icons that list a group's tags by hand and unused groups
- Add the `minus`, `plus` and `x` groups and use them in
`alarm-clock-minus`, `file-minus`, `file-plus` and `file-x`
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(icons): apply tag groups to variant icons
Add 23 groups for common variant suffixes and use them in 89 icons, removing
the literal tags a group now provides. No icon loses a resolved tag; 65
icons gain tags from their group.
Groups: alert, check, clock, code, cog, currency, dot, heart, key, lock,
off, pause, pen, percent, play, question-mark, search, slash, sparkles,
star, stop, sync, user.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Add favorite
* feat(icons): extend base icon metadata in variant icons
Use `$extends:<base>` in 703 variant icons whose tags or categories already
include every value of their base icon, and remove the 1952 tags and 1058
categories the base now provides. The base is the longest prefix of the icon
name that is an icon, e.g. `clipboard-pen-line` extends `clipboard-pen`.
- `tags` extend their base in 417 icons, `categories` in 675
- Only applied where the base adds nothing new, so the resolved tags and
categories of every icon are unchanged
- `contributors` are left as they are
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBLsKpZMoKKKVeibcuRXnC
* Apply batched suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Karsa <contact@karsa.org>
* Apply feedback
* Updated groups
* Apply updates
* Apply more extends and groups
* Stricter checks
* Remove more duplicates
* Update tags
* Apply batched suggestions from code review
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Fix tags
* feat(metadata): reusable tag groups with `$group:`
A `$group:<group-name>` marker in `tags` is replaced in place with the tags of
`groups/<group-name>.json`: a small set of tags for what a variant suffix
means, such as `-minus` → remove, delete.
- Read groups in `readGroups` and resolve markers next to `$extends:`,
failing on unknown groups and on `$group:` outside `tags`
- Add `group.schema.json` and lint, spell-check and format `groups/*.json`
- `pnpm checkIcons` also fails on literals a group already provides, and
warns about icons that list a group's tags by hand and unused groups
- Add the `minus`, `plus` and `x` groups and use them in
`alarm-clock-minus`, `file-minus`, `file-plus` and `file-x`
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(icons): apply tag groups to variant icons
Add 23 groups for common variant suffixes and use them in 89 icons, removing
the literal tags a group now provides. No icon loses a resolved tag; 65
icons gain tags from their group.
Groups: alert, check, clock, code, cog, currency, dot, heart, key, lock,
off, pause, pen, percent, play, question-mark, search, slash, sparkles,
star, stop, sync, user.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Add favorite
* feat(icons): extend base icon metadata in variant icons
Use `$extends:<base>` in 703 variant icons whose tags or categories already
include every value of their base icon, and remove the 1952 tags and 1058
categories the base now provides. The base is the longest prefix of the icon
name that is an icon, e.g. `clipboard-pen-line` extends `clipboard-pen`.
- `tags` extend their base in 417 icons, `categories` in 675
- Only applied where the base adds nothing new, so the resolved tags and
categories of every icon are unchanged
- `contributors` are left as they are
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBLsKpZMoKKKVeibcuRXnC
* Apply batched suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Karsa <contact@karsa.org>
* Apply feedback
* Updated groups
* Apply updates
* Apply more extends and groups
* Stricter checks
* Remove more duplicates
* Update tags
* Apply batched suggestions from code review
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Fix tags
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(metadata): reusable tag groups with `$group:`
A `$group:<group-name>` marker in `tags` is replaced in place with the tags of
`groups/<group-name>.json`: a small set of tags for what a variant suffix
means, such as `-minus` → remove, delete.
- Read groups in `readGroups` and resolve markers next to `$extends:`,
failing on unknown groups and on `$group:` outside `tags`
- Add `group.schema.json` and lint, spell-check and format `groups/*.json`
- `pnpm checkIcons` also fails on literals a group already provides, and
warns about icons that list a group's tags by hand and unused groups
- Add the `minus`, `plus` and `x` groups and use them in
`alarm-clock-minus`, `file-minus`, `file-plus` and `file-x`
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Fix group checker
* Apply batched suggestions from code review
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(metadata): reusable tag groups with `$group:`
A `$group:<group-name>` marker in `tags` is replaced in place with the tags of
`groups/<group-name>.json`: a small set of tags for what a variant suffix
means, such as `-minus` → remove, delete.
- Read groups in `readGroups` and resolve markers next to `$extends:`,
failing on unknown groups and on `$group:` outside `tags`
- Add `group.schema.json` and lint, spell-check and format `groups/*.json`
- `pnpm checkIcons` also fails on literals a group already provides, and
warns about icons that list a group's tags by hand and unused groups
- Add the `minus`, `plus` and `x` groups and use them in
`alarm-clock-minus`, `file-minus`, `file-plus` and `file-x`
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Fix group checker
* Apply batched suggestions from code review
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(metadata): reusable tag groups with `$group:`
A `$group:<group-name>` marker in `tags` is replaced in place with the tags of
`groups/<group-name>.json`: a small set of tags for what a variant suffix
means, such as `-minus` → remove, delete.
- Read groups in `readGroups` and resolve markers next to `$extends:`,
failing on unknown groups and on `$group:` outside `tags`
- Add `group.schema.json` and lint, spell-check and format `groups/*.json`
- `pnpm checkIcons` also fails on literals a group already provides, and
warns about icons that list a group's tags by hand and unused groups
- Add the `minus`, `plus` and `x` groups and use them in
`alarm-clock-minus`, `file-minus`, `file-plus` and `file-x`
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Fix group checker
* Apply batched suggestions from code review
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(metadata): reusable tag groups with `$group:`
A `$group:<group-name>` marker in `tags` is replaced in place with the tags of
`groups/<group-name>.json`: a small set of tags for what a variant suffix
means, such as `-minus` → remove, delete.
- Read groups in `readGroups` and resolve markers next to `$extends:`,
failing on unknown groups and on `$group:` outside `tags`
- Add `group.schema.json` and lint, spell-check and format `groups/*.json`
- `pnpm checkIcons` also fails on literals a group already provides, and
warns about icons that list a group's tags by hand and unused groups
- Add the `minus`, `plus` and `x` groups and use them in
`alarm-clock-minus`, `file-minus`, `file-plus` and `file-x`
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Fix group checker
* Apply batched suggestions from code review
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(metadata): reuse metadata from a base icon with `$extends:`
Icon metadata can reference a base icon with an inline `$extends:<icon-name>`
marker in `tags`, `categories` or `contributors`. The marker is replaced in
place with the base icon's resolved values at build time.
- Resolve markers in `readAllMetadata` and `getIconMetaData`, failing on
unknown icons, self references and circular chains
- Accept markers in `icon.schema.json`
- `pnpm checkIcons` fails on duplicate markers and on literals a marker
already provides, and runs in CI
- Docs read the resolved metadata index instead of the raw icon JSON
- Use `$extends:alarm-clock` in `alarm-clock-minus`
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(metadata): reuse metadata from a base icon with `$extends:`
Icon metadata can reference a base icon with an inline `$extends:<icon-name>`
marker in `tags`, `categories` or `contributors`. The marker is replaced in
place with the base icon's resolved values at build time.
- Resolve markers in `readAllMetadata` and `getIconMetaData`, failing on
unknown icons, self references and circular chains
- Accept markers in `icon.schema.json`
- `pnpm checkIcons` fails on duplicate markers and on literals a marker
already provides, and runs in CI
- Docs read the resolved metadata index instead of the raw icon JSON
- Use `$extends:alarm-clock` in `alarm-clock-minus`
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Fix comments
* Format code
* Update tools/build-helpers/src/resolveMetadataExtends.ts
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Fix resolving icon metadata
* feat(metadata): reuse metadata from a base icon with `$extends:`
Icon metadata can reference a base icon with an inline `$extends:<icon-name>`
marker in `tags`, `categories` or `contributors`. The marker is replaced in
place with the base icon's resolved values at build time.
- Resolve markers in `readAllMetadata` and `getIconMetaData`, failing on
unknown icons, self references and circular chains
- Accept markers in `icon.schema.json`
- `pnpm checkIcons` fails on duplicate markers and on literals a marker
already provides, and runs in CI
- Docs read the resolved metadata index instead of the raw icon JSON
- Use `$extends:alarm-clock` in `alarm-clock-minus`
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Fix comments
* Format code
* Update tools/build-helpers/src/resolveMetadataExtends.ts
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Fix resolving icon metadata
* Fix comments
* Format code
* Update tools/build-helpers/src/resolveMetadataExtends.ts
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore(deps): bump the build-deps group across 1 directory with 2 updates
Bumps the build-deps group with 2 updates in the / directory: [typescript](https://github.com/microsoft/TypeScript) and [rollup](https://github.com/rollup/rollup).
Updates `typescript` from 6.0.3 to 7.0.2
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v6.0.3...v7.0.2)
Updates `rollup` from 4.63.1 to 4.65.0
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.63.1...v4.65.0)
---
updated-dependencies:
- dependency-name: typescript
dependency-version: 7.0.2
dependency-type: direct:development
update-type: version-update:semver-major
dependency-group: build-deps
- dependency-name: rollup
dependency-version: 4.65.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: build-deps
...
Signed-off-by: dependabot[bot] <support@github.com>
* chore(deps): keep typescript on v6 and ignore v7 major updates
TypeScript 7 (the native port) ships without the JS compiler API, which
typescript-eslint, rollup-plugin-dts, svelte-package/svelte-check, astro
check, the Angular compiler, vue-tsc and @vue/compiler-sfc all depend on.
Revert typescript to ^6.0.3, keep the rollup bump, and tell dependabot to
skip typescript major updates until the ecosystem supports v7.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eric <eric.fennis@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(ci): move icon preview markup into a composite action
Move generateChangedIconsCommentMarkup.mts into .github/actions/icon-preview
as its own pnpm workspace package, following the suggest-metadata action.
svgson and @lucide/helpers are now real dependencies installed with a
filtered pnpm install, replacing the ad-hoc `npm install svgson --force`.
Paths are resolved from the repo root relative to the script, so it also
works through `pnpm --filter @lucide/icon-preview-action start`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ci): skip icon preview comment when no icons changed
When a pull request only deletes icons, CHANGED_FILES has no SVGs and the
script printed "No changed icons found" to stdout, which was then posted as
the preview comment.
The script now logs that to stderr. The action writes no markup file and
sets `has-changes=false`, the workflow skips the artifact upload, and the
comment workflow skips its steps when no artifact exists instead of failing
on the missing artifact.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* ci(icon-preview): post the preview comment from a pull_request_target workflow
Like the PR title workflow, the icon preview now runs on pull_request_target
and posts its comment directly, so comment-icon-preview.yml and the
workflow_run artifact hand-off are removed.
The job checks out the base branch and runs the action from there. The
changed SVGs are read from the pull request head with `git fetch` + `git show`
into a temp directory, so no code from the pull request is checked out or run.
File names are checked against `icons/<kebab-case>.svg` before use.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ci): store metadata suggestions in the review instead of actions/cache
pull_request_target runs can't write to the Actions cache ("cache write
denied: token has no writable scopes"), so the cache was never saved and
the model was asked again on every push. That produced new suggestions
anchored on the same lines as earlier ones.
Store the model's suggestions per icon name as hidden JSON in the review
body and read them back from the bot's earlier reviews, so the API is only
called for icons without stored suggestions. This drops the gh api and
actions/cache steps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSdUq9uFpjuQsoobCBqz4j
* fix(ci): handle earlier metadata suggestion comments on older PRs
- Read values from the oldest comment format too, which doesn't start with
"Suggested `field`" and was posted without a closing fence; the field
is taken from the suggestion's `"field": [` line.
- Skip a field while an earlier suggestion for it is still applicable (not
outdated), so two suggestions never replace overlapping lines.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSdUq9uFpjuQsoobCBqz4j
* fix(ci): read icon metadata at the reviewed commit
The review comments are anchored to the commit that triggered the run,
but the icon JSON was read through the PR files' raw_url, which always
points at the latest head. When a commit was pushed while the action
was running, suggestions were anchored to the wrong lines, e.g. a
use-cases block replacing the "tags": [ line, producing invalid JSON.
Read each file at the reviewed commit instead, default that commit to the
PR's head SHA, and skip files that don't exist at it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSdUq9uFpjuQsoobCBqz4j
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(ci): run metadata suggestions on every PR update with cached results
- Run on opened/reopened/synchronize and queue runs per PR.
- Cache the model's suggestions per icon name with actions/cache, so the
OpenAI API is only called again for icons that are added or renamed.
- Skip suggestions that were already posted instead of skipping the whole
PR once it has a review.
- Suggest only the values to append after the last array item instead of
the whole array; empty and inline arrays still get the full block.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSdUq9uFpjuQsoobCBqz4j
* refactor(ci): key the suggestions cache on the PR's icon names
Replace the separate restore/save steps and the `cache-updated` output
with a single actions/cache step keyed on a hash of the changed icon
files, so the API is only called again when icon names change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSdUq9uFpjuQsoobCBqz4j
* style(ci): drop trailing blank line in suggest-metadata action
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSdUq9uFpjuQsoobCBqz4j
* fix(ci): dedupe metadata suggestions by value instead of comment body
The append-style suggestion includes the array's current last item, so
comparing whole comment bodies reposted the same values after a
contributor added a tag or applied part of a suggestion. Read the values
back from the bot's earlier suggestion blocks per file and field, and
exclude them like the current values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSdUq9uFpjuQsoobCBqz4j
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(scripts): keep contributors out of metadata suggestions
The shared metadata instructions include a Contributors section asking to
add the PR author, but the suggestion schema has no contributors field, so
the model leaked it into use-cases. Strip that section from the prompt and
explicitly restrict suggestions to tags, categories and use-cases.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MThCx7cVXV3XWfo5C327Ci
* refactor(ci): move metadata suggestions into a dedicated composite action
Move scripts/suggestMetaData.mts to .github/actions/suggest-metadata and
wrap it in a composite action with explicit inputs. The workflow now uses
the local action; package.json scripts point to the new location.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MThCx7cVXV3XWfo5C327Ci
* chore(ci): add .env.example for testing the suggest-metadata action locally
Document the env vars the script reads, point the debug/watch scripts at
an action-local .env, and fix the USE_FILE_SYSYEM typo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MThCx7cVXV3XWfo5C327Ci
* chore(ci): make .github/actions a pnpm workspace
Add .github/actions/* to the pnpm workspace and give the suggest-metadata
action its own package.json with its dependencies (openai, zod, dotenv,
@octokit/rest). openai, zod and dotenv move out of the root package.json;
@octokit/rest stays there for updateContributors. The workflow now only
installs the action's dependencies, and the root suggest:metadata scripts
delegate to the action package.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MThCx7cVXV3XWfo5C327Ci
* Upgrade to gpt-6-luna
---------
Co-authored-by: Claude <noreply@anthropic.com>
* test(svelte): add Vite and SvelteKit integration fixtures
Scaffold a plain Svelte + Vite app (create-vite svelte-ts) and a SvelteKit
app (sv create --template minimal) that consume the built @lucide/svelte
package. Each fixture runs a production build, svelte-check and a Vitest
Browser Mode test, wired up via test:integrations:svelte and CI.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0198HmSjcE6LwPPScwLdMtjt
* test(svelte): label Pen as canonical and Edit2 as alias in fixtures
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0198HmSjcE6LwPPScwLdMtjt
---------
Co-authored-by: Claude <noreply@anthropic.com>
* ci: split lint-code into ESLint, Prettier and JSON Schema jobs with PR annotations
- ESLint: annotations via setup-node's built-in eslint-stylish problem matcher.
- Prettier: new scripts/lintFormat.mts formats unformatted files in memory, diffs
them and emits line-level `::error` annotations with the expected code.
- JSON Schema: replace ajv-cli with a local ESLint rule (Ajv draft 2020-12) that
validates icons/categories JSON against their `$schema` and reports at the
offending property. eslint-plugin-json-schema-validator was not used because it
runs Ajv in draft-07 mode and ignores `dependentRequired`.
All annotations use workflow commands, so they work on fork PRs with a read-only token.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVxEtqNswEDq1N23taZYh8
* Improve root value check and property error handling
Enhance error reporting for JSON schema validation.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* style(lint): format jsonSchemaPlugin with Prettier
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVxEtqNswEDq1N23taZYh8
* test commit
* fix(lint): annotate syntax errors from Prettier and ESLint in PRs
- ESLint/JSON Schema jobs use a small GitHub formatter that prints `::error`
workflow commands. setup-node's eslint-stylish matcher skipped parsing errors
because they have no rule id.
- lintFormat.mts annotates Prettier syntax errors (exit code 2) at their line and
column instead of only printing stderr, and still checks the other files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVxEtqNswEDq1N23taZYh8
* ci(lint): spell check icon and category metadata with cspell annotations
- New "Spelling" job runs scripts/lintSpelling.mts, which turns cspell findings
into `::error` annotations (with cspell's suggested fix when it has one).
- cspell accepts both US and British English, ignores contributor usernames and
deprecated `alias.typo` aliases.
- Add existing search tags and domain terms to .cspell/custom-words.txt so the
current metadata passes.
- Add `lint:spelling` to `pnpm lint` and lint-staged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVxEtqNswEDq1N23taZYh8
* fix(lint): pin JSON schemas per file set and drop typo tags
- json-schema/no-invalid now takes the schema from config (icon.schema.json for
icons, category.schema.json for categories) and reports a `$schema` that
points elsewhere, so metadata can't opt out of validation.
- Remove the misspelled `frigerator` and `sterisk` tags and their allowlist entries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVxEtqNswEDq1N23taZYh8
* fix(icons): split joined-word tags into separate words
Tags like `groupchat` and `timemachine` become `group chat` and `time machine`
so they read naturally and pass the spell check without allowlisting. Where the
spaced tag already existed (`smart home`) the joined one is dropped.
cspell now skips all alias names, since they are icon names that can't change
(e.g. the deprecated `palmtree` alias).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FVxEtqNswEDq1N23taZYh8
* Revert
* Try another error
* Revert error
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Updates needed to support Solid 2
* Switch to the next tag
* feat(packages): add `@lucide/solid` package for Solid 2
Solid 2's API is a breaking change from Solid 1 and is still an RC, so
`lucide-solid` stays on Solid 1 (`solid-js: ^1.4.7`) untouched and Solid 2
support ships as a new `packages/solid` package named `@lucide/solid` —
the same scheme already used by `@lucide/svelte` and `@lucide/angular`.
The package is a copy of `lucide-solid` on the `@lucide/shared` build
architecture, ported to Solid 2:
- `splitProps` -> `omit`, `solid-js/web` -> `@solidjs/web`,
`JSXElement` -> `Element`, `Context.Provider` -> `<Context value>`
- `jsxImportSource` is `@solidjs/web` in tsconfig, the esbuild step and
`ts.createProgram`; rollup externals drop the removed `solid-js/store`
- `vite-plugin-solid` -> `@solidjs/vite-plugin`
- tests keep upstream's coverage with two Solid 2 adaptations: boolean
attribute literals must be strings (`aria-hidden="false"`), and writes
are batched so the reactivity tests need a `flush()` before asserting
Rendered output is unchanged — the snapshots are byte-identical to
`lucide-solid`'s.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(solid): fix React leftovers in the Solid guide
The Solid guide was derived from the React guide and three things were
not carried across:
- `advanced/typescript.md` still told readers the types are for "a
TypeScript React project"
- its `LucideIcon` example kept React's destructured-props signature.
The `: ButtonProps` annotation had also been dropped in the copy, so
`ButtonProps` was declared but unused and the parameters were
implicitly `any`. Destructuring props breaks reactivity in Solid —
verified: with `({ icon: Icon, label })` a changing `label` never
reaches the DOM, while `props.label` updates correctly.
- `advanced/aliased-names.md` had an orphaned `###` heading; its parent
`## Choosing import name style` was dropped during the conversion
because it documents React-only subpath exports.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: type the shared Sandpack wrapper with `SandpackProps`
The base Sandpack wrapper declared its props as
`FunctionalComponent<LucideProps>` with `LucideProps` imported from
`@lucide/vue`, which has nothing to do with a Sandpack embed. None of
the props the markdown plugin actually passes (`template`, `files`,
`options`, `customSetup`, `theme`) were declared, so they only reached
the inner `<Sandpack />` via Vue's fallthrough attributes.
Declare `SandpackProps` from `sandpack-vue3` instead and forward them
explicitly. This also removes the `@lucide/vue` import that made every
framework guide — including the Solid one, which uses this wrapper
directly — appear to reference the Vue package.
Verified with a full `vitepress build`: each framework guide's chunk
still carries its own template (react/vue/vite-svelte/vanilla/
vite-solid) and all five props reach the component.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(solid): document the Solid 2 flavour in the getting started guide
The Solid guide teaches `lucide-solid` throughout (64 references), so
the one-line note about `@lucide/solid` left Solid 2 readers on a page
whose every install command and live example is Solid 1 — and the
packages list points `@lucide/solid` at this same page.
Replace that note with a "Using Solid 2?" callout holding its own
tabbed install group, the import-specifier swap, and a table of the
Solid 2 changes that affect code written around Lucide (`@solidjs/web`,
`jsxImportSource`, `splitProps` -> `omit`, `<Ctx value>`), linking out
to Solid's own migration guide for the rest.
The claim that both packages expose the same components and props is
checked, not assumed: `dist/types/lucide-solid.d.ts` is identical
between the two packages, and the only differences anywhere in the
public type surface are Solid's own renames (the `JSX` namespace source
and `JSXElement` -> `Element`).
Keeps one guide section for both packages, matching how upstream
handles the two Svelte packages.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(solid): port lucide-solid to Solid 2
- Icon/context/types use Solid 2 APIs: omit() instead of splitProps(),
Dynamic and JSX types from @solidjs/web.
- solid-js / @solidjs/web 2.0 (rc) as peer and dev dependencies,
babel-preset-solid 2; rollup and tsconfig updated to match.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Add integration test
* Update rollup file
* Fix tests
* Revert lucide-solid changes
* update lockfile
* Fix docs
* Format code
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Eric <eric.fennis@gmail.com>
* fix(github/actions): move to daily release flow with gated releases on other changes
* Update .github/workflows/ci.yml
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
* Apply suggestion from @karsa-mistmere
---------
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
* test(lucide-solid): add Vite and SolidStart integration fixtures
Mirror the `lucide-react` integration suite for `lucide-solid`. Both
fixtures are scaffolded by `pnpm create solid` and consume the built
package through its public entry points only:
- `integrations/lucide-solid/vite` (vanilla/basic template)
- `integrations/lucide-solid/solid-start` (solid-start-v2/basic template)
Each runs a production build, `tsc --noEmit`, and a Vitest Browser Mode
test in Chromium under a `check` script, wired into the new root
`test:integrations:solid` script and an `integration` job on the
`lucide-solid` workflow.
The showcase exercises the bare export, per-icon props, `LucideProvider`,
an alias against its canonical icon, and the `lucide-solid/icons/circle`
deep import. `lucide-solid` has no `DynamicIcon`, so the deep import
takes the place of the React fixtures' dynamic-icon block. The SolidStart
spec additionally navigates between two routes.
Both fixtures resolve the package through its `solid` export condition
and compile the raw JSX in `dist/source`; the SolidStart build bundles it
into the SSR server chunk.
Each fixture carries a second TypeScript project,
`tsconfig.declarations.json`, that compiles the showcase with
`skipLibCheck: false` so the declarations shipped in `dist/types` are
actually read. That catches a `.d.ts` pointing at a file the build never
emitted, which the app typecheck silently degrades to `any`.
`@solidjs/start@2` requires Vite 8 while the repository pins Vite 7, so
the SolidStart fixture is released from that pin by a parent-scoped
`'@lucide/integration-solid-solid-start>vite'` override. Only that
importer resolves to Vite 8; every other package stays on 7.3.6.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAmVJZqdypLNwEFhCmfsPN
* fix(solid): align SolidStart fixture with Vite 7
* Upgrade vite dependency from 7.3.6 to 8.3.2
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Update README.md to clarify Vite overrides
Removed duplicate note about bug #4846 and added a section on Vite overrides.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Mirror the lucide-react integration tests for `@lucide/vue`. Each fixture is
scaffolded with the framework's official generator (`pnpm create vue@latest`,
`pnpm create nuxt@latest`, `pnpm create vike@latest --vue`), trimmed of demo
boilerplate, and runs a production build, `vue-tsc`, and a Vitest Browser Mode
test in Chromium that renders the public `@lucide/vue` APIs (static icon,
per-instance props, `setLucideProps` context, alias vs canonical icon).
Nuxt is pinned to 4.4.8: 4.5.x requires Vite 8, which the workspace-wide
`vite: ^7.3.6` override does not allow.
Wire it up with a `test:integrations:vue` root script and an `integration`
job in the Lucide Vue workflow.
Claude-Session: https://claude.ai/code/session_01KmQsnSECZoNxh1GJ15BmdH
Co-authored-by: Claude <noreply@anthropic.com>
* Add metadata information
* improve contribution
* feat(docs): added new contribution guide
* feat(docs): minor linting fix
* feat(docs): update dead link
* feat(docs): change layout, some stylistic change, et cetera
* feat(docs): alter metadata example layout
* feat(docs): update titles, split use case & tag guide
* feat(docs): finetune some design guide images & make
* feat(docs): finetune some design guide images & add icons to use case guide
* feat(docs): added ux writing instructions & proofread markdowns to match these guidelines
* feat(docs): updated design language / specification descriptions to better distinguish what is what
* feat(docs): updated design language / specification descriptions to better distinguish what is what
* feat(docs): changed some languages again
* fix(icons): add design principle do/dont about diagonal lines
* fix(icons): decrease blurriness of ideal pixel perfection pixel preview
* fix(icons): remove color from pixel perfection previews
* fix(icons): remove color from pixel perfection previews
* fix(icons): reword symmerical icon rule
* fix(icons): fix calendar design in density rule image
* fix(icons): slightly adjust centered stroke rule text
* Rename 'useCases' property to 'use-cases'
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Update allowed SVG elements in code conventions
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: update icon design discussion template
Remove name and description fields from icon design template
* Add title field to icon design discussion template
* Add description for icon request template
Added a description field for icon requests.
* Update icon-design.yml
* Format code
---------
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
* ci(security): add scoped GITHUB_TOKEN permissions to all workflows
Every workflow now declares its token permissions explicitly instead of
inheriting the repository default, which satisfies OpenSSF Scorecard's
token-permissions check.
Multi-job and write-scoped workflows use `permissions: {}` as a
deny-by-default floor at the top level, with the actual grants declared
per job. Consolidating those grants at the workflow level would leak
scopes into jobs that don't need them — a `permissions` block replaces
rather than merges, so the empty top-level block also makes a forgotten
job block fail closed rather than inherit the default.
Single-job read-only package workflows keep a plain top-level
`contents: read`; there is no write scope to leak, so a second block per
file would only add noise.
Scopes were matched to what each job's steps actually do:
- linting-icons: `contents: read` per job. No `pull-requests: read` —
tj-actions/changed-files only needs that with `use_rest_api: true`,
and here it reads the local .git.
- lint-pr-title: `pull-requests: read`, which
action-semantic-pull-request needs to read the PR title.
- close-stale-prs: `pull-requests: write` only; the job never checks out.
- comment-icon-preview: `actions: read` for the cross-run artifact
download, plus `pull-requests: write` to post the comment.
- release: `contents: read` for the checkouts, `id-token: write` only on
the publishing jobs that use npm provenance, and `contents: write` only
on post-release, which uploads the release assets. The prepare job
neither checks out nor calls the API, so its empty scope is explicit.
- ci: `contents: write` only on create-release; the reusable-workflow
calls pass `id-token: write` and `contents: write` down to release.yml,
since a called workflow can only reduce what the caller grants.
Verified with actionlint (no new findings) and zizmor (no
excessive-permissions findings).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(ci): correct pull_request path filter in the lucide-vue workflow
The Vue package lives in packages/vue, not packages/lucide-vue, so the
filter never matched and the workflow did not run for changes to the
package itself.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Remove empty scope in prepare
* Patch WeaknessCWE-506
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>