-
-
Notifications
You must be signed in to change notification settings - Fork 629
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added github comment icon workflow (#946)
* feat: added github comment icon workflow * feat: improved segment node styling * feat: improved segment node styling * fix: fixed grid alignment issue * chore: cleanup * fix: added ref forwarding to SvgPreview component * chore: removed svg preview from icon detail overlay * chore: updated tj-actions/changed-files * chore: switched to pull_request_target * chore: simplified path segment highlighting logic * Fixes incorrect relative links in documentation pages (#973) * Fixes incorrect relative links in documentation pages * Unifies documentation page names to avoid 404 links --------- Co-authored-by: Karsa <[email protected]> * Add forklift icon (#943) * Fix vercel build * Add forklift icon Co-Authored-By: willythewizard <[email protected]> --------- Co-authored-by: willythewizard <[email protected]> * adds utility-pole icon (#971) Co-authored-by: Karsa <[email protected]> * 📦 Bump lucide package versions to 0.123.0 * Adds `nfc` icons (#960) * added nfc icons * fixes smartphone-nfc * Update icons/nfc.svg Co-authored-by: Jakob Guddas <[email protected]> * Update icons/smartphone-nfc.svg Co-authored-by: Jakob Guddas <[email protected]> --------- Co-authored-by: Karsa <[email protected]> Co-authored-by: Jakob Guddas <[email protected]> * 📦 Bump lucide package versions to 0.124.0 * fix: updated pnpm-lock.yaml * fix: added missing api endpoint file * chore: fixed nextjs path name parsing in production * chore: only run workflow when path includes icons/*.svg * chore: added Cache-Control header to gh-icon api route response * feat: added dark mode support to gh-icon * feat: switched to using picture tag for gh-icon * feat: added space between gh-icons in pr comment * fix: changed icon size base back to 24x24 * feat: added title to gh-icon comment image * fix: changed gh-icon url * chore: added groups with class names * feat: improved shadow masking * Removes need for building duplicate icons by supporting CSS based dark mode * chore: resolved type issues * feat: changed image width from 48% to 400px --------- Co-authored-by: Karsa <[email protected]> Co-authored-by: Karsa <[email protected]> Co-authored-by: Eric Fennis <[email protected]> Co-authored-by: willythewizard <[email protected]> Co-authored-by: Lucide Bot <[email protected]>
- Loading branch information
1 parent
76ce22e
commit 93cfd3d
Showing
8 changed files
with
523 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
name: Pull request comment | ||
|
||
on: | ||
pull_request_target: | ||
paths: | ||
- 'icons/*.svg' | ||
|
||
permissions: | ||
pull-requests: write | ||
contents: write | ||
|
||
jobs: | ||
Explore-GitHub-Actions: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
with: | ||
fetch-depth: 0 | ||
ref: refs/pull/${{ github.event.pull_request.number }}/merge | ||
- name: Get changed files | ||
id: changed-files | ||
uses: tj-actions/changed-files@v35 | ||
with: | ||
files: icons/*.svg | ||
- name: Generate comment | ||
id: generate-comment | ||
run: | | ||
delimiter="$(openssl rand -hex 8)" | ||
echo "body<<$delimiter" >> $GITHUB_OUTPUT | ||
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do | ||
cat "$file" | # get file content | ||
tr '\n' ' ' | # remove line breaks | ||
sed -e 's/<svg[^>]*>/<svg>/g' | # remove attributes from svg element | ||
base64 -w 0 | # encode svg | ||
sed "s|.*|<img width=\"400\" title=\"$file\" alt=\"$file\" src=\"https://lucide.dev/api/gh-icon/&.svg\"/> |" | ||
done | tr '\n' ' ' >> $GITHUB_OUTPUT | ||
echo >> $GITHUB_OUTPUT | ||
echo "$delimiter" >> $GITHUB_OUTPUT | ||
- name: Find Comment | ||
uses: peter-evans/find-comment@v2 | ||
id: fc | ||
with: | ||
issue-number: ${{ github.event.pull_request.number }} | ||
comment-author: 'github-actions[bot]' | ||
body-includes: Added or changed icons | ||
- name: Create or update comment | ||
uses: peter-evans/create-or-update-comment@v2 | ||
with: | ||
comment-id: ${{ steps.fc.outputs.comment-id }} | ||
issue-number: ${{ github.event.pull_request.number }} | ||
body: | | ||
Added or changed icons | ||
${{ steps.generate-comment.outputs.body }} | ||
edit-mode: replace |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
import React from 'react'; | ||
import { PathProps, Path } from './types'; | ||
import { getPaths, assert } from './utils'; | ||
|
||
const Grid = ({ | ||
radius, | ||
fill, | ||
...props | ||
}: { | ||
strokeWidth: number; | ||
radius: number; | ||
} & PathProps<'stroke', 'strokeWidth'>) => ( | ||
<g className="svg-preview-grid-group" strokeLinecap="butt" {...props}> | ||
<rect | ||
width={24 - props.strokeWidth} | ||
height={24 - props.strokeWidth} | ||
x={props.strokeWidth / 2} | ||
y={props.strokeWidth / 2} | ||
rx={radius} | ||
fill={fill} | ||
/> | ||
<path | ||
d={ | ||
props.d || | ||
new Array(Math.floor(24 - 1)) | ||
.fill(null) | ||
.flatMap((_, i) => [ | ||
`M${props.strokeWidth} ${i + 1}h${24 - props.strokeWidth * 2}`, | ||
`M${i + 1} ${props.strokeWidth}v${24 - props.strokeWidth * 2}`, | ||
]) | ||
.join('') | ||
} | ||
/> | ||
</g> | ||
); | ||
|
||
const Shadow = ({ | ||
radius, | ||
paths, | ||
...props | ||
}: { | ||
radius: number; | ||
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<number, Path[]>) | ||
); | ||
return ( | ||
<> | ||
<g className="svg-preview-shadow-mask-group" {...props}> | ||
{groupedPaths.map(([id, paths]) => ( | ||
<mask | ||
id={`svg-preview-shadow-mask-${id}`} | ||
maskUnits="userSpaceOnUse" | ||
strokeOpacity="1" | ||
strokeWidth={props.strokeWidth} | ||
stroke="#000" | ||
> | ||
<rect x={0} y={0} width={24} height={24} fill="#fff" stroke="none" rx={radius} /> | ||
<path | ||
d={paths | ||
.flatMap(({ prev, next }) => [ | ||
`M${prev.x} ${prev.y}h.01`, | ||
`M${next.x} ${next.y}h.01`, | ||
]) | ||
.filter((val, idx, arr) => arr.indexOf(val) === idx) | ||
.join('')} | ||
/> | ||
</mask> | ||
))} | ||
</g> | ||
<g className="svg-preview-shadow-group" {...props}> | ||
{paths.map(({ d, c: { id } }, i) => ( | ||
<path key={i} mask={`url(#svg-preview-shadow-mask-${id})`} d={d} /> | ||
))} | ||
<path | ||
d={paths | ||
.flatMap(({ prev, next }) => [`M${prev.x} ${prev.y}h.01`, `M${next.x} ${next.y}h.01`]) | ||
.filter((val, idx, arr) => arr.indexOf(val) === idx) | ||
.join('')} | ||
/> | ||
</g> | ||
</> | ||
); | ||
}; | ||
|
||
const ColoredPath = ({ | ||
colors, | ||
paths, | ||
...props | ||
}: { paths: Path[]; colors: string[] } & PathProps<never, 'd' | 'stroke'>) => ( | ||
<g className="svg-preview-colored-path-group" {...props}> | ||
{paths.map(({ d, c }, i) => ( | ||
<path key={i} d={d} stroke={colors[(c.name === 'path' ? i : c.id) % colors.length]} /> | ||
))} | ||
</g> | ||
); | ||
|
||
const ControlPath = ({ | ||
paths, | ||
radius, | ||
pointSize, | ||
...props | ||
}: { pointSize: number; paths: Path[]; radius: number } & PathProps< | ||
'stroke' | 'strokeWidth', | ||
'd' | ||
>) => { | ||
const controlPaths = paths.map((path, i) => { | ||
const element = paths.filter((p) => p.c.id === path.c.id); | ||
const lastElement = element.at(-1)?.next; | ||
assert(lastElement); | ||
const isClosed = element[0].prev.x === lastElement.x && element[0].prev.y === lastElement.y; | ||
const showMarker = !['rect', 'circle', 'ellipse'].includes(path.c.name); | ||
return { | ||
...path, | ||
showMarker, | ||
startMarker: showMarker && path.isStart && !isClosed, | ||
endMarker: showMarker && paths[i + 1]?.isStart !== false && !isClosed, | ||
}; | ||
}); | ||
return ( | ||
<> | ||
<g | ||
className="svg-preview-control-path-marker-mask-group" | ||
strokeWidth={pointSize} | ||
stroke="#000" | ||
> | ||
{controlPaths.map(({ prev, next, showMarker }, i) => { | ||
return ( | ||
showMarker && ( | ||
<mask | ||
id={`svg-preview-control-path-marker-mask-${i}`} | ||
key={i} | ||
maskUnits="userSpaceOnUse" | ||
> | ||
<rect x="0" y="0" width="24" height="24" fill="#fff" stroke="none" rx={radius} /> | ||
<path d={`M${prev.x} ${prev.y}h.01`} /> | ||
<path d={`M${next.x} ${next.y}h.01`} /> | ||
</mask> | ||
) | ||
); | ||
})} | ||
</g> | ||
<g className="svg-preview-control-path-group" {...props}> | ||
{controlPaths.map(({ d, showMarker }, i) => ( | ||
<path | ||
key={i} | ||
mask={showMarker ? `url(#svg-preview-control-path-marker-mask-${i})` : undefined} | ||
d={d} | ||
/> | ||
))} | ||
</g> | ||
<g className="svg-preview-control-path-marker-group" {...props}> | ||
<path | ||
d={controlPaths | ||
.flatMap(({ prev, next, showMarker }) => | ||
showMarker ? [`M${prev.x} ${prev.y}h.01`, `M${next.x} ${next.y}h.01`] : [] | ||
) | ||
.join('')} | ||
/> | ||
{controlPaths.map(({ d, prev, next, startMarker, endMarker }, i) => ( | ||
<React.Fragment key={i}> | ||
{startMarker && <circle cx={prev.x} cy={prev.y} r={pointSize / 2} />} | ||
{endMarker && <circle cx={next.x} cy={next.y} r={pointSize / 2} />} | ||
</React.Fragment> | ||
))} | ||
</g> | ||
</> | ||
); | ||
}; | ||
|
||
const SvgPreview = React.forwardRef<SVGSVGElement, { src: string; showGrid?: boolean }>( | ||
({ src, showGrid = false }, ref) => { | ||
const paths = getPaths(src); | ||
const darkModeCss = `@media screen and (prefers-color-scheme: dark) { | ||
.svg-preview-grid-group, | ||
.svg-preview-shadow-mask-group, | ||
.svg-preview-shadow-group { | ||
stroke: #fff; | ||
} | ||
}`; | ||
return ( | ||
<svg | ||
ref={ref} | ||
xmlns="http://www.w3.org/2000/svg" | ||
width={24} | ||
height={24} | ||
viewBox="0 0 24 24" | ||
fill="none" | ||
stroke="currentColor" | ||
strokeWidth={2} | ||
strokeLinecap="round" | ||
strokeLinejoin="round" | ||
> | ||
<style>{darkModeCss}</style> | ||
{showGrid && <Grid strokeWidth={0.1} stroke="#777" strokeOpacity={0.3} radius={1} />} | ||
<Shadow paths={paths} strokeWidth={4} stroke="#777" radius={1} strokeOpacity={0.15} /> | ||
<ColoredPath | ||
paths={paths} | ||
colors={[ | ||
'#1982c4', | ||
'#4267AC', | ||
'#6a4c93', | ||
'#B55379', | ||
'#FF595E', | ||
'#FF7655', | ||
'#ff924c', | ||
'#FFAE43', | ||
'#ffca3a', | ||
'#C5CA30', | ||
'#8ac926', | ||
'#52A675', | ||
]} | ||
/> | ||
<ControlPath radius={1} paths={paths} pointSize={1} stroke="#fff" strokeWidth={0.125} /> | ||
</svg> | ||
); | ||
} | ||
); | ||
|
||
export default SvgPreview; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { SVGProps } from 'react'; | ||
import { getCommands } from './utils'; | ||
|
||
export type Point = { x: number; y: number }; | ||
|
||
export type Path = { | ||
d: string; | ||
prev: Point; | ||
next: Point; | ||
isStart: boolean; | ||
c: ReturnType<typeof getCommands>[number]; | ||
}; | ||
|
||
export type PathProps< | ||
RequiredProps extends keyof SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>, | ||
NeverProps extends keyof SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement> | ||
> = Required<Pick<React.SVGProps<SVGElement & SVGRectElement & SVGCircleElement>, RequiredProps>> & | ||
Omit< | ||
React.SVGProps<SVGPathElement & SVGRectElement & SVGCircleElement>, | ||
RequiredProps & NeverProps | ||
>; |
Oops, something went wrong.