Skip to content

Add built-in Lucide icon library for node styling#1777

Merged
kmcginnes merged 6 commits into
aws:mainfrom
jkemmererupgrade:feature/lucide-icon-picker
Jun 3, 2026
Merged

Add built-in Lucide icon library for node styling#1777
kmcginnes merged 6 commits into
aws:mainfrom
jkemmererupgrade:feature/lucide-icon-picker

Conversation

@jkemmererupgrade
Copy link
Copy Markdown
Contributor

Description

Adds the Lucide icon library as a built-in source for node icons, introducing a searchable picker in the Node Styling panel alongside the existing custom-upload button.

Key architectural decision (per #1774): icon selections are stored as symbolic lucide:<name> references rather than data URIs. Resolution to a renderable form happens at render time, keeping config and export files human-readable and allowing the picker to highlight the current selection without reverse-engineering a blob.

Resolution at render time uses two paths depending on the consumer:

  • React components (VertexIcon) — a new useResolvedIconUrl hook backed by React Query (staleTime: Infinity) converts lucide:<name> to a base64 SVG data URI on first use and caches the result per session. Plain URLs and data: URIs pass through via initialData, so they render immediately without an async round-trip.
  • Cytoscape canvas (renderNode)getLucideSvgString resolves the icon directly from the lucide-react dynamic-import map, skipping any network fetch.

Custom-uploaded icons (data: URIs) are completely unaffected — both resolvers pass them through unchanged.

The IconPicker component renders a searchable popover (8-column icon grid, 50-icon default window, type-to-filter). It emits the symbolic lucide:<name> ref so NodeStyleDialog stores the portable reference.

This commit also fixes a pre-existing lint-staged misconfiguration where the root oxlint --fix task matched vitest.config.ts, which oxlint then silently ignored (per its own ignorePatterns), causing a spurious exit-code 1 on pre-commit. The root lint-staged config now mirrors the oxlint ignore patterns.

This is Slice 1 of the 3-slice split of #1589, per @kmcginnes review.

Validation

All four local gates pass:

  • pnpm run checks — oxlint, oxfmt, and TypeScript type-check all clean
  • pnpm test — 1738 / 1738 tests pass (45 new tests across 4 new test files)
  • pnpm coverage — all thresholds met; new files have ~100% line coverage (branches floor bumped 44 to 45 by the new branch coverage)
  • pnpm build — production build completes without warnings

To validate the picker manually: open the Node Styling panel for any vertex type, click "Browse", search for an icon name (e.g. "plane"), select it, and confirm the node label updates in the graph view. Re-opening the dialog should show the selected icon highlighted in the grid.

Related Issues

Addresses #1774
Slice 1 of #1589

Check List

  • I confirm that my contribution is made under the terms of the Apache 2.0 license.
  • I have verified pnpm checks passes with no errors.
  • I have verified pnpm test passes with no failures.
  • I have covered new added functionality with unit tests if necessary.
  • I have updated documentation if necessary. (docs/features/graph-view.md)

Stores icon selections as symbolic lucide:<name> references rather than
data URIs, so config files remain human-readable and the picker can
highlight the current selection without reverse-engineering the blob.

Resolution happens at render time via two paths:
- React components (VertexIcon): useResolvedIconUrl hook backed by
  React Query (staleTime: Infinity) converts lucide:<name> to a base64
  SVG data URI on first use and caches the result.
- Cytoscape pipeline (renderNode): getLucideSvgString resolves directly
  from the lucide-react dynamic-import map, skipping any fetch round-trip.

Custom-uploaded icons (data: URIs and plain URLs) are unchanged -- they
pass through both resolvers untouched.

The new IconPicker component (searchable popover, 8-column grid, 50-icon
default window) is wired into NodeStyleDialog alongside the existing file
upload button so users can choose a Lucide icon or keep uploading their own.

Also fixes a pre-existing lint-staged misconfiguration where the root
oxlint --fix task matched vitest.config.ts, which oxlint then ignored
(matching its own ignorePatterns), causing exit code 1. The root
lint-staged config now mirrors the oxlint ignorePatterns by routing
*.config.* files to oxfmt only and restricting oxlint to packages/.

Pre-flight: pnpm checks, pnpm test (1738/1738), pnpm coverage all pass.
This is Slice 1 of the 3-slice split of aws#1589 per @kmcginnes review.
Copy link
Copy Markdown
Collaborator

@kmcginnes kmcginnes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking pretty good. Thanks for splitting it up for me. I've provided a bunch of changes you can easily take, but there is likely some cleanup needed afterward. I think once these changes go in this PR will be much simpler. And feel free to push back on any of my suggested changes if you disagree.

One other feature that would be nice, but is not strictly necessary, is paging within the icon picker. I don't expect many people to browse through 60 pages of icons, but if they are searching and get 100 results I could see paging being useful.

Long term (not right now), I imagine we will add icon categories so the user can more easily narrow in on a specific icon they might want without having to know the name, similar to Apple's emoji picker. And if we wanted to be extra fancy we could do vertical scrolling in the grid with a virtualized list for performance instead of using paging. But paging is simpler and more pragmatic for now.

Comment thread packages/graph-explorer/src/components/IconPicker.tsx Outdated
Comment thread packages/graph-explorer/src/components/IconPicker.tsx Outdated
Comment thread packages/graph-explorer/src/components/IconPicker.tsx Outdated
Comment thread packages/graph-explorer/src/components/IconPicker.tsx Outdated
Comment thread packages/graph-explorer/src/components/IconPicker.tsx Outdated
Comment thread packages/graph-explorer/src/components/VertexIcon.tsx Outdated
Comment thread packages/graph-explorer/src/components/VertexIcon.tsx Outdated
Comment thread packages/graph-explorer/src/components/VertexIcon.tsx Outdated
Comment thread docs/features/graph-view.md Outdated
Comment thread packages/graph-explorer/src/utils/lucideIcons.ts
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
jkemmererupgrade added a commit to jkemmererupgrade/graph-explorer that referenced this pull request May 28, 2026
Add three buttons to the General Settings page:
- Export: downloads current vertex and edge styling as graph-explorer-styling.json
- Import: loads a JSON file, applies it as the live styling baseline, and stores it in
  defaultStylingAtom so per-type Reset on the node style dialog restores to imported
  values rather than application defaults
- Reset All: reverts every vertex and edge to the imported baseline (or application
  defaults if no file has been imported this session)

Introduce defaultStyling.ts with a Zod schema (DefaultStylingSchema), parseDefaultStyling,
resolveDefaultStyling (converts lucide:<name> shorthand to iconUrl references established
by the Lucide icon picker in PR aws#1777), and userStylingToExportFormat. The file format
round-trips cleanly via lucide:<name> refs.

Add mergeDefaultsIntoUserStyling to userPreferences.ts so imported baselines fill gaps
for unstyled types without overwriting user customisations.

Update toJsonFileData with an optional indent param (defaults unchanged) so the exported
file is human-readable.

Add unit tests for the schema, parse/resolve pipeline, hooks, and reset behaviour.
Update docs/features/settings.md and docs/features/graph-view.md accordingly.

Slice 2 of the 3-slice split of aws#1589.
Stacks on aws#1777 (Lucide icon picker).
jkemmererupgrade added a commit to jkemmererupgrade/graph-explorer that referenced this pull request May 28, 2026
…rkers

useResolvedIconUrl is no longer used after VertexIcon switched to DynamicIcon
per maintainer feedback on aws#1777.
@kmcginnes
Copy link
Copy Markdown
Collaborator

@jkemmererupgrade FYI, I had some high priority issues come up which has delayed this a bit. Once I knock those out I'll circle back to this.

kmcginnes added 2 commits June 2, 2026 18:02
- Remove unused useResolvedIconUrl hook and resolveIconUrl/lucideIconToDataUri
- Rename lucideIconUrl.ts to lucideIcons.ts and export IconName type and
  allIconNamesSorted so consumers share a single source of truth
- Make isLucideIconRef a proper type guard, eliminating non-null assertion
- Fix renderNode to resolve lucide: refs regardless of iconImageType
- Fix IconPicker truncation hint to show whenever results are capped
- Add tests for new exports, error paths, and edge cases
…icker

# Conflicts:
#	packages/graph-explorer/package.json
@kmcginnes
Copy link
Copy Markdown
Collaborator

kmcginnes commented Jun 2, 2026

@jkemmererupgrade I'm running through some final tweaks. But this is looking good.

kmcginnes
kmcginnes previously approved these changes Jun 2, 2026
@kmcginnes kmcginnes merged commit 0c6a9c6 into aws:main Jun 3, 2026
5 of 8 checks passed
@kmcginnes kmcginnes linked an issue Jun 3, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add built-in Lucide icon library for node styling

2 participants