Compare commits
2 Commits
main
...
feat/wordm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63c26284dc | ||
|
|
beab14f535 |
131
.github/ISSUE_TEMPLATE/add_monochrome_icon.yml
vendored
131
.github/ISSUE_TEMPLATE/add_monochrome_icon.yml
vendored
@@ -1,73 +1,88 @@
|
||||
name: "Add light & dark icon"
|
||||
description: Use this template to add a new icon to the project. Monochrome icons need both light and dark versions.
|
||||
title: "feat(icons): add [NAME]"
|
||||
labels: ["monochrome-icon"]
|
||||
description: |
|
||||
Submit a new icon with distinct 'light' and 'dark' versions.
|
||||
- A **Light Icon** is typically light-colored (e.g., predominantly white/very light shades, like Sonarr's logo) and is best suited for dark backgrounds.
|
||||
- A **Dark Icon** is typically dark-colored (e.g., predominantly black/very dark shades, like Ollama's logo) and is best suited for light backgrounds.
|
||||
title: "feat(icons): add monochrome [icon-name]"
|
||||
labels: ["icon addition", "monochrome icon"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Hello and thank you for contributing to the project! Please fill out the following information to add a new icon to the project.
|
||||
Once you've submitted the issue, sombody from the team will review it, before adding a label which automatically creates a pull request with the other filetypes.
|
||||
If you submit a PNG icon, please note, that the SVG can not be generated from it.
|
||||
Thanks for contributing a new icon with light and dark variants! Please complete the following.
|
||||
Ensure your icons are optimized and adhere to quality standards.
|
||||
**Light Icon Definition:** Predominantly light colors (e.g., white, light grays - like Sonarr's logo), designed for optimal visibility on dark backgrounds.
|
||||
**Dark Icon Definition:** Predominantly dark colors (e.g., black, dark grays - like Ollama's logo), designed for optimal visibility on light backgrounds.
|
||||
|
||||
- type: input
|
||||
id: icon-name
|
||||
attributes:
|
||||
label: Icon name
|
||||
description: The name has to be unique and should be kebab-case.
|
||||
placeholder: e.g. "icon-name"
|
||||
label: "Icon Name"
|
||||
description: "The unique name for the icon (e.g., `home-assistant`). This will be the base filename."
|
||||
placeholder: "e.g., sonarr"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: svg-code-light
|
||||
attributes:
|
||||
label: Paste light mode icon
|
||||
description: |
|
||||
Please paste the icon here. It will automatically upload it to github. This icon should be visible on a light background.
|
||||
label: "SVG Code (Light Version)"
|
||||
description: "Paste the SVG code for the **Light Version** of the icon (predominantly light colors, for dark backgrounds)."
|
||||
placeholder: "<svg>...</svg>"
|
||||
render: svg
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: svg-code-dark
|
||||
attributes:
|
||||
label: Paste dark mode icon
|
||||
description: |
|
||||
Please paste the icon here. It will automatically upload it to github. This icon should be visible on a dark background.
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Icon type
|
||||
options:
|
||||
- SVG
|
||||
- PNG
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Categories
|
||||
multiple: true
|
||||
options:
|
||||
- Animal
|
||||
- Cloud
|
||||
- Communication
|
||||
- Design
|
||||
- Development
|
||||
- E-Commerce
|
||||
- Education
|
||||
- File
|
||||
- Finance
|
||||
- Food
|
||||
- Gaming
|
||||
- Hardware
|
||||
- Health
|
||||
- Location
|
||||
- Logistics
|
||||
- Media
|
||||
- Music
|
||||
- Nature
|
||||
- News
|
||||
- Organization
|
||||
- Search
|
||||
- Security
|
||||
- SocialMedia
|
||||
- Streaming
|
||||
- Travel
|
||||
- Video
|
||||
label: "SVG Code (Dark Version)"
|
||||
description: "Paste the SVG code for the **Dark Version** of the icon (predominantly dark colors, for light backgrounds)."
|
||||
placeholder: "<svg>...</svg>"
|
||||
render: svg
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: brand-guidelines
|
||||
attributes:
|
||||
label: Aliases
|
||||
description: A comma separated list of aliases
|
||||
placeholder: e.g. "icon-alias, icon-alias-2"
|
||||
label: "Brand Guidelines URL (Optional)"
|
||||
description: "If available, provide a link to the official brand guidelines or logo source."
|
||||
placeholder: "e.g., https://brand.home-assistant.io/"
|
||||
|
||||
- type: input
|
||||
id: source-url
|
||||
attributes:
|
||||
label: "Icon Source URL (Optional)"
|
||||
description: "Link to where you found the icon (e.g., official website, GitHub repository)."
|
||||
placeholder: "e.g., https://example.com/logo.svg"
|
||||
|
||||
- type: dropdown
|
||||
id: icon-type-confirmation
|
||||
attributes:
|
||||
label: "Icon Type Confirmation"
|
||||
description: "Confirm this icon submission includes separate light and dark variants as defined above."
|
||||
options:
|
||||
- "Yes, this includes light and dark variants."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: categories
|
||||
attributes:
|
||||
label: "Categories (Optional, comma-separated)"
|
||||
description: "Relevant categories for the icon (e.g., `media, automation`). Helps with searching and organization."
|
||||
placeholder: "e.g., dashboard, utility, networking"
|
||||
|
||||
- type: input
|
||||
id: aliases
|
||||
attributes:
|
||||
label: "Aliases (Optional, comma-separated)"
|
||||
description: "Alternative names or keywords for the icon (e.g., `ha, hass`)."
|
||||
placeholder: "e.g., prox, pve"
|
||||
|
||||
- type: textarea
|
||||
id: additional-notes
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: |
|
||||
Add additional informations like a link to the application.
|
||||
label: "Additional Notes (Optional)"
|
||||
description: "Any other relevant information or context for the maintainers."
|
||||
|
||||
119
.github/ISSUE_TEMPLATE/add_normal_icon.yml
vendored
119
.github/ISSUE_TEMPLATE/add_normal_icon.yml
vendored
@@ -1,71 +1,74 @@
|
||||
name: "Add normal icon"
|
||||
description: Use this template to add a new icon to the project. Normal icons work for both light and dark themes.
|
||||
title: "feat(icons): add [NAME]"
|
||||
labels: ["normal-icon"]
|
||||
description: "Submit a new 'normal' icon. A normal icon is designed to be versatile and visually consistent across both light and dark backgrounds without requiring separate versions."
|
||||
title: "feat(icons): add [icon-name]"
|
||||
labels: ["icon addition", "normal icon"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Hello and thank you for contributing to the project! Please fill out the following information to add a new icon to the project.
|
||||
Once you've submitted the issue, sombody from the team will review it, before adding a label which automatically creates a pull request with the other filetypes.
|
||||
If you submit a PNG icon, please note, that the SVG can not be generated from it.
|
||||
Thanks for your interest in contributing a new icon! Please complete the following details.
|
||||
A **normal icon** should be designed to work well and maintain good contrast/readability on both light and dark backgrounds.
|
||||
Ensure your icon is optimized and adheres to our quality standards.
|
||||
|
||||
- type: input
|
||||
id: name
|
||||
id: icon-name
|
||||
attributes:
|
||||
label: Icon name
|
||||
description: The name has to be unique and should be kebab-case.
|
||||
placeholder: e.g. "icon-name"
|
||||
label: "Icon Name"
|
||||
description: "The unique name for the icon (e.g., `home-assistant`). This will be used as the filename."
|
||||
placeholder: "e.g., sonarr"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: icon
|
||||
id: svg-code
|
||||
attributes:
|
||||
label: Paste icon
|
||||
description: |
|
||||
Please paste the icon here. It will automatically upload it to github.
|
||||
- type: dropdown
|
||||
id: type
|
||||
attributes:
|
||||
label: Icon type
|
||||
options:
|
||||
- SVG
|
||||
- PNG
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Categories
|
||||
multiple: true
|
||||
options:
|
||||
- Animal
|
||||
- Cloud
|
||||
- Communication
|
||||
- Design
|
||||
- Development
|
||||
- E-Commerce
|
||||
- Education
|
||||
- File
|
||||
- Finance
|
||||
- Food
|
||||
- Gaming
|
||||
- Hardware
|
||||
- Health
|
||||
- Location
|
||||
- Logistics
|
||||
- Media
|
||||
- Music
|
||||
- Nature
|
||||
- News
|
||||
- Organization
|
||||
- Search
|
||||
- Security
|
||||
- SocialMedia
|
||||
- Streaming
|
||||
- Travel
|
||||
- Video
|
||||
label: "SVG Code (Normal Version)"
|
||||
description: "Paste the SVG code for the icon. This version should be suitable for both light and dark backgrounds. Ensure the icon\'s colors provide good contrast and readability on various background shades."
|
||||
placeholder: "<svg>...</svg>"
|
||||
render: svg
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: brand-guidelines
|
||||
attributes:
|
||||
label: Aliases
|
||||
description: A comma separated list of aliases
|
||||
placeholder: e.g. "icon-alias, icon-alias-2"
|
||||
label: "Brand Guidelines URL (Optional)"
|
||||
description: "If available, provide a link to the official brand guidelines or logo source."
|
||||
placeholder: "e.g., https://brand.home-assistant.io/"
|
||||
|
||||
- type: input
|
||||
id: source-url
|
||||
attributes:
|
||||
label: "Icon Source URL (Optional)"
|
||||
description: "Link to where you found the icon (e.g., official website, GitHub repository)."
|
||||
placeholder: "e.g., https://example.com/logo.svg"
|
||||
|
||||
- type: dropdown
|
||||
id: icon-type-confirmation
|
||||
attributes:
|
||||
label: "Icon Type Confirmation"
|
||||
description: "Confirm this icon is a \'normal\' icon (designed to be versatile and visually consistent across both light and dark backgrounds without requiring separate versions)."
|
||||
options:
|
||||
- "Yes, this is a normal icon."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: categories
|
||||
attributes:
|
||||
label: "Categories (Optional, comma-separated)"
|
||||
description: "Relevant categories for the icon (e.g., `media, automation`). Helps with searching and organization."
|
||||
placeholder: "e.g., dashboard, utility, networking"
|
||||
|
||||
- type: input
|
||||
id: aliases
|
||||
attributes:
|
||||
label: "Aliases (Optional, comma-separated)"
|
||||
description: "Alternative names or keywords for the icon (e.g., `ha, hass`)."
|
||||
placeholder: "e.g., prox, pve"
|
||||
|
||||
- type: textarea
|
||||
id: additional-notes
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: |
|
||||
Add additional informations like a link to the application.
|
||||
label: "Additional Notes (Optional)"
|
||||
description: "Any other relevant information or context for the maintainers."
|
||||
|
||||
110
.github/ISSUE_TEMPLATE/add_wordmark_icon.yml
vendored
Normal file
110
.github/ISSUE_TEMPLATE/add_wordmark_icon.yml
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
name: "Add Wordmark Icon"
|
||||
description: |
|
||||
Submit a new wordmark icon (icon featuring brand name).
|
||||
A **Wordmark Icon** incorporates the brand's name or logotype directly into or alongside the graphical mark.
|
||||
It can be a single version for all themes, or have separate light/dark versions.
|
||||
- **Light Icon Version:** (If applicable) predominantly light colors (e.g., Sonarr's logo), for dark backgrounds.
|
||||
- **Dark Icon Version:** (If applicable) predominantly dark colors (e.g., Ollama's logo), for light backgrounds.
|
||||
title: "feat(icons): add wordmark [icon-name]"
|
||||
labels: ["icon addition", "wordmark icon"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for contributing a new wordmark icon!
|
||||
**What is a Wordmark Icon?** It's an icon that integrates the brand's name/logotype with its graphical mark for stronger brand recognition.
|
||||
Please complete the following details. Ensure your icon is optimized.
|
||||
|
||||
- type: input
|
||||
id: icon-name
|
||||
attributes:
|
||||
label: "Icon Name (Base)"
|
||||
description: "The unique name for the icon (e.g., `my-service-wordmark`). This will be used as the base for filenames."
|
||||
placeholder: "e.g., awesome-app-wordmark"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**Icon Versioning:** Does this wordmark have a single version or separate light/dark versions?
|
||||
|
||||
- type: checkboxes
|
||||
id: wordmark-type
|
||||
attributes:
|
||||
label: "Wordmark Version Type"
|
||||
description: "Select how this wordmark is provided."
|
||||
options:
|
||||
- label: "Single version (works on both light and dark backgrounds)"
|
||||
required: false
|
||||
- label: "Separate light and dark versions"
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: svg-code-wordmark-normal
|
||||
attributes:
|
||||
label: "SVG Code (Single/Normal Wordmark)"
|
||||
description: "If this is a single-version wordmark, paste the SVG code here. Ensure it's versatile for all backgrounds."
|
||||
placeholder: "<svg>...</svg>"
|
||||
render: svg
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: svg-code-wordmark-light
|
||||
attributes:
|
||||
label: "SVG Code (Light Wordmark Version)"
|
||||
description: "If providing separate versions, paste the SVG for the **Light Version** here (predominantly light colors, e.g., for dark UIs)."
|
||||
placeholder: "<svg>...</svg>"
|
||||
render: svg
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: svg-code-wordmark-dark
|
||||
attributes:
|
||||
label: "SVG Code (Dark Wordmark Version)"
|
||||
description: "If providing separate versions, paste the SVG for the **Dark Version** here (predominantly dark colors, e.g., for light UIs)."
|
||||
placeholder: "<svg>...</svg>"
|
||||
render: svg
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**Important:** Please ensure you fill out the correct SVG code section(s) based on your selection above. If providing separate light/dark versions, both are typically required. If a single version, only the 'Single/Normal' field is needed.
|
||||
|
||||
- type: input
|
||||
id: brand-guidelines
|
||||
attributes:
|
||||
label: "Brand Guidelines URL (Optional)"
|
||||
description: "Link to official brand guidelines, if available."
|
||||
placeholder: "e.g., https://brand.example.com/"
|
||||
|
||||
- type: input
|
||||
id: source-url
|
||||
attributes:
|
||||
label: "Icon Source URL (Optional)"
|
||||
description: "Link to where you found the wordmark (e.g., official website)."
|
||||
placeholder: "e.g., https://example.com/logo-wordmark.svg"
|
||||
|
||||
- type: input
|
||||
id: categories
|
||||
attributes:
|
||||
label: "Categories (Optional, comma-separated)"
|
||||
description: "Relevant categories for the icon."
|
||||
placeholder: "e.g., service, productivity"
|
||||
|
||||
- type: input
|
||||
id: aliases
|
||||
attributes:
|
||||
label: "Aliases (Optional, comma-separated)"
|
||||
description: "Alternative names or keywords."
|
||||
placeholder: "e.g., myapp-text, full-logo"
|
||||
|
||||
- type: textarea
|
||||
id: additional-notes
|
||||
attributes:
|
||||
label: "Additional Notes (Optional)"
|
||||
description: "Any other relevant information."
|
||||
@@ -1,36 +1,59 @@
|
||||
name: "Update light & dark icon"
|
||||
description: Use this template to update an existing icon. Monochrome icons need both light and dark versions.
|
||||
title: "feat(icons): update [NAME]"
|
||||
labels: ["monochrome-icon"]
|
||||
description: |
|
||||
Improve an existing icon with distinct 'light' and 'dark' versions.
|
||||
- A **Light Icon** is typically light-colored (e.g., predominantly white/very light shades, like Sonarr's logo) and is best suited for dark backgrounds.
|
||||
- A **Dark Icon** is typically dark-colored (e.g., predominantly black/very dark shades, like Ollama's logo) and is best suited for light backgrounds.
|
||||
title: "fix(icons): update monochrome [icon-name]"
|
||||
labels: ["icon update", "monochrome icon"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Hello and thank you for contributing to the project! Please fill out the following informations to update an existing icon in the project.
|
||||
Thanks for improving an icon with light and dark variants!
|
||||
Please ensure the updated versions adhere to the definitions:
|
||||
**Light Icon Definition:** Predominantly light colors (e.g., white, light grays - like Sonarr's logo), for dark backgrounds.
|
||||
**Dark Icon Definition:** Predominantly dark colors (e.g., black, dark grays - like Ollama's logo), for light backgrounds.
|
||||
Please specify the icon name and provide the updated SVGs.
|
||||
|
||||
- type: input
|
||||
id: iconName
|
||||
id: icon-name
|
||||
attributes:
|
||||
label: Icon name
|
||||
description: The name has to be unique and should be kebab-case.
|
||||
placeholder: e.g. "icon-name"
|
||||
label: "Icon Name to Update"
|
||||
description: "The name of the existing icon you are updating (e.g., `home-assistant`)."
|
||||
placeholder: "e.g., sonarr"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: svg-code-light
|
||||
attributes:
|
||||
label: Paste light mode icon
|
||||
description: |
|
||||
Please paste the icon here. It will automatically upload it to github. This icon should be visible on a light background.
|
||||
label: "Updated SVG Code (Light Version)"
|
||||
description: "Paste the new SVG code for the **Light Version** (predominantly light colors, for dark backgrounds)."
|
||||
placeholder: "<svg>...</svg>"
|
||||
render: svg
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: svg-code-dark
|
||||
attributes:
|
||||
label: Paste dark mode icon
|
||||
description: |
|
||||
Please paste the icon here. It will automatically upload it to github. This icon should be visible on a dark background.
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Icon type
|
||||
options:
|
||||
- SVG
|
||||
- PNG
|
||||
label: "Updated SVG Code (Dark Version)"
|
||||
description: "Paste the new SVG code for the **Dark Version** (predominantly dark colors, for light backgrounds)."
|
||||
placeholder: "<svg>...</svg>"
|
||||
render: svg
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reason-for-update
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: |
|
||||
Add additional informations like, the reason for the update, or what has been changed.
|
||||
label: "Reason for Update"
|
||||
description: "Briefly explain why this icon needs an update (e.g., outdated design, quality, color adjustment)."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: additional-notes
|
||||
attributes:
|
||||
label: "Additional Notes (Optional)"
|
||||
description: "Any other relevant information or context for the maintainers."
|
||||
|
||||
53
.github/ISSUE_TEMPLATE/update_normal_icon.yml
vendored
53
.github/ISSUE_TEMPLATE/update_normal_icon.yml
vendored
@@ -1,31 +1,44 @@
|
||||
name: "Update normal icon"
|
||||
description: Use this template to update an existing icon. Normal icons work for both light and dark themes.
|
||||
title: "feat(icons): update [NAME]"
|
||||
labels: ["normal-icon"]
|
||||
description: "Improve an existing 'normal' icon. A normal icon is designed to be versatile and visually consistent across both light and dark backgrounds."
|
||||
title: "fix(icons): update [icon-name]"
|
||||
labels: ["icon update", "normal icon"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Hello and thank you for contributing to the project! Please fill out the following informations to update an existing icon in the project.
|
||||
Thanks for helping improve an existing icon!
|
||||
A **normal icon** should be designed to work well and maintain good contrast/readability on both light and dark backgrounds.
|
||||
Please specify the icon name and provide the updated details.
|
||||
|
||||
- type: input
|
||||
id: iconName
|
||||
id: icon-name
|
||||
attributes:
|
||||
label: Icon name
|
||||
description: The name has to match the existing icon name.
|
||||
placeholder: e.g. "icon-name"
|
||||
label: "Icon Name to Update"
|
||||
description: "The name of the existing icon you are updating (e.g., `home-assistant`)."
|
||||
placeholder: "e.g., sonarr"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: svg-code
|
||||
attributes:
|
||||
label: Paste icon
|
||||
description: |
|
||||
Please paste the icon here. It will automatically upload it to github.
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Icon type
|
||||
options:
|
||||
- SVG
|
||||
- PNG
|
||||
label: "Updated SVG Code (Normal Version)"
|
||||
description: "Paste the new SVG code for the icon. This version should be suitable for both light and dark backgrounds. Ensure the icon\'s colors provide good contrast and readability."
|
||||
placeholder: "<svg>...</svg>"
|
||||
render: svg
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reason-for-update
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: |
|
||||
Add additional informations like, the reason for the update, or what has been changed.
|
||||
label: "Reason for Update"
|
||||
description: "Briefly explain why this icon needs an update (e.g., outdated design, quality improvement, color adjustment for better universal background compatibility)."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: additional-notes
|
||||
attributes:
|
||||
label: "Additional Notes (Optional)"
|
||||
description: "Any other relevant information or context for the maintainers."
|
||||
|
||||
87
.github/ISSUE_TEMPLATE/update_wordmark_icon.yml
vendored
Normal file
87
.github/ISSUE_TEMPLATE/update_wordmark_icon.yml
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
name: "Update Wordmark Icon"
|
||||
description: |
|
||||
Improve an existing wordmark icon.
|
||||
A **Wordmark Icon** incorporates the brand's name or logotype.
|
||||
Remember the light/dark definitions if applicable for the wordmark.
|
||||
title: "fix(icons): update wordmark [icon-name]"
|
||||
labels: ["icon update", "wordmark icon"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for helping improve a wordmark icon!
|
||||
Please specify the icon name and provide the updated details.
|
||||
|
||||
- type: input
|
||||
id: icon-name
|
||||
attributes:
|
||||
label: "Wordmark Icon Name to Update"
|
||||
description: "The name of the existing wordmark icon you are updating (e.g., `my-service-wordmark`)."
|
||||
placeholder: "e.g., awesome-app-wordmark"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**Icon Versioning Being Updated:** Is this wordmark a single version or does it have separate light/dark versions? This helps in providing the correct updated SVGs.
|
||||
|
||||
- type: checkboxes
|
||||
id: wordmark-update-type
|
||||
attributes:
|
||||
label: "Wordmark Version Type (of the icon being updated)"
|
||||
description: "Indicate the type of wordmark you are updating."
|
||||
options:
|
||||
- label: "Updating a single-version wordmark"
|
||||
required: false
|
||||
- label: "Updating a wordmark with separate light and dark versions"
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: svg-code-wordmark-normal-updated
|
||||
attributes:
|
||||
label: "Updated SVG Code (Single/Normal Wordmark)"
|
||||
description: "If updating a single-version wordmark, paste the NEW SVG code here."
|
||||
placeholder: "<svg>...</svg>"
|
||||
render: svg
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: svg-code-wordmark-light-updated
|
||||
attributes:
|
||||
label: "Updated SVG Code (Light Wordmark Version)"
|
||||
description: "If updating light/dark versions, paste the NEW SVG for the Light Version here (predominantly light colors)."
|
||||
placeholder: "<svg>...</svg>"
|
||||
render: svg
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: svg-code-wordmark-dark-updated
|
||||
attributes:
|
||||
label: "Updated SVG Code (Dark Wordmark Version)"
|
||||
description: "If updating light/dark versions, paste the NEW SVG for the Dark Version here (predominantly dark colors)."
|
||||
placeholder: "<svg>...</svg>"
|
||||
render: svg
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**Important:** Provide the updated SVG(s) in the relevant section(s) above based on the type of wordmark being updated.
|
||||
|
||||
- type: textarea
|
||||
id: reason-for-update
|
||||
attributes:
|
||||
label: "Reason for Update"
|
||||
description: "Briefly explain why this wordmark icon needs an update (e.g., outdated design, quality improvement, color adjustment)."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: additional-notes
|
||||
attributes:
|
||||
label: "Additional Notes (Optional)"
|
||||
description: "Any other relevant information."
|
||||
@@ -1,22 +1,12 @@
|
||||
import { IconDetails } from "@/components/icon-details"
|
||||
import { BASE_URL, WEB_URL } from "@/constants"
|
||||
import { getAllIcons, getAuthorData } from "@/lib/api"
|
||||
import { formatIconName } from "@/lib/utils"
|
||||
import type { Metadata, ResolvingMetadata } from "next"
|
||||
import { default as dynamicImport } from "next/dynamic"
|
||||
import { notFound } from "next/navigation"
|
||||
export const dynamicParams = false
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const iconsData = await getAllIcons()
|
||||
if (process.env.CI_MODE === "false") {
|
||||
// This is meant to speed up the build process in local development
|
||||
return Object.keys(iconsData)
|
||||
.slice(0, 5)
|
||||
.map((icon) => ({
|
||||
icon,
|
||||
}))
|
||||
}
|
||||
return Object.keys(iconsData).map((icon) => ({
|
||||
icon,
|
||||
}))
|
||||
|
||||
122
web/src/components/icon-actions.tsx
Normal file
122
web/src/components/icon-actions.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { Check, Copy, Download, Github, Link as LinkIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import type React from "react";
|
||||
|
||||
export type IconActionsProps = {
|
||||
imageUrl: string;
|
||||
githubUrl: string;
|
||||
iconName: string;
|
||||
format: string;
|
||||
variantKey: string;
|
||||
copiedUrlKey: string | null;
|
||||
copiedImageKey: string | null;
|
||||
handleDownload: (event: React.MouseEvent, url: string, filename: string) => Promise<void>;
|
||||
handleCopyUrl: (url: string, variantKey: string, event?: React.MouseEvent) => void;
|
||||
handleCopyImage: (imageUrl: string, format: string, variantKey: string, event?: React.MouseEvent) => Promise<void>;
|
||||
};
|
||||
|
||||
export function IconActions({
|
||||
imageUrl,
|
||||
githubUrl,
|
||||
iconName,
|
||||
format,
|
||||
variantKey,
|
||||
copiedUrlKey,
|
||||
copiedImageKey,
|
||||
handleDownload,
|
||||
handleCopyUrl,
|
||||
handleCopyImage,
|
||||
}: IconActionsProps) {
|
||||
const downloadFilename = `${iconName}.${format}`;
|
||||
const isUrlCopied = copiedUrlKey === variantKey;
|
||||
const isImageCopied = copiedImageKey === variantKey;
|
||||
|
||||
return (
|
||||
<TooltipProvider delayDuration={300}>
|
||||
<div className="flex gap-2 mt-3 w-full justify-center">
|
||||
{/* Download Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-lg cursor-pointer"
|
||||
onClick={(e) => handleDownload(e, imageUrl, downloadFilename)}
|
||||
aria-label={`Download ${iconName} as ${format.toUpperCase()}`}
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Download {format.toUpperCase()}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
{/* Copy Image Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-lg cursor-pointer"
|
||||
onClick={(e) => handleCopyImage(imageUrl, format, variantKey, e)}
|
||||
aria-label={`Copy ${iconName} image as ${format.toUpperCase()}`}
|
||||
>
|
||||
{isImageCopied ? (
|
||||
<Check className="w-4 h-4 text-green-500" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Copy image to clipboard</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
{/* Copy URL Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-lg cursor-pointer"
|
||||
onClick={(e) => handleCopyUrl(imageUrl, variantKey, e)}
|
||||
aria-label={`Copy direct URL for ${iconName} ${format.toUpperCase()}`}
|
||||
>
|
||||
{isUrlCopied ? (
|
||||
<Check className="w-4 h-4 text-green-500" />
|
||||
) : (
|
||||
<LinkIcon className="w-4 h-4" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Copy direct URL</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
{/* View on GitHub Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="outline" size="icon" className="h-8 w-8 rounded-lg" asChild>
|
||||
<Link
|
||||
href={githubUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={`View ${iconName} ${format} file on GitHub`}
|
||||
>
|
||||
<Github className="w-4 h-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>View on GitHub</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import { formatIconName } from "@/lib/utils"
|
||||
import type { Icon } from "@/types/icons"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import { preload } from "react-dom"
|
||||
|
||||
export function IconCard({
|
||||
name,
|
||||
@@ -21,7 +20,7 @@ export function IconCard({
|
||||
<Link prefetch={false} href={`/icons/${name}`} className="group flex flex-col items-center p-3 sm:p-4 cursor-pointer">
|
||||
<div className="relative h-16 w-16 mb-2">
|
||||
<Image
|
||||
src={`${BASE_URL}/${iconData.base}/${name}.${iconData.base}`}
|
||||
src={`${BASE_URL}/${iconData.base}/${iconData.colors?.light || name}.${iconData.base}`}
|
||||
alt={`${name} icon`}
|
||||
fill
|
||||
className="object-contain p-1 group-hover:scale-110 transition-transform duration-300"
|
||||
|
||||
@@ -10,16 +10,122 @@ import { formatIconName } from "@/lib/utils"
|
||||
import type { AuthorData, Icon, IconFile } from "@/types/icons"
|
||||
import confetti from "canvas-confetti"
|
||||
import { motion } from "framer-motion"
|
||||
import { ArrowRight, Check, Copy, Download, FileType, Github, Moon, PaletteIcon, Sun } from "lucide-react"
|
||||
import dynamic from "next/dynamic"
|
||||
import { ArrowRight, Check, FileType, Github, Moon, PaletteIcon, Sun, Type } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import type React from "react"
|
||||
import { useCallback, useState } from "react"
|
||||
import { toast } from "sonner"
|
||||
import { Carbon } from "./carbon"
|
||||
import { IconActions } from "./icon-actions"
|
||||
import { MagicCard } from "./magicui/magic-card"
|
||||
import { Badge } from "./ui/badge"
|
||||
|
||||
type RenderVariantFn = (
|
||||
format: string,
|
||||
iconName: string,
|
||||
theme?: "light" | "dark"
|
||||
) => React.ReactNode
|
||||
|
||||
type IconVariantsSectionProps = {
|
||||
title: string
|
||||
description: string
|
||||
iconElement: React.ReactNode
|
||||
aavailableFormats: string[]
|
||||
icon: string
|
||||
iconData: Icon
|
||||
handleCopy: (url: string, variantKey: string, event?: React.MouseEvent) => void
|
||||
handleDownload: (event: React.MouseEvent, url: string, filename: string) => Promise<void>
|
||||
copiedVariants: Record<string, boolean>
|
||||
theme?: "light" | "dark"
|
||||
renderVariant: RenderVariantFn
|
||||
}
|
||||
|
||||
function IconVariantsSection({
|
||||
title,
|
||||
description,
|
||||
iconElement,
|
||||
aavailableFormats,
|
||||
icon,
|
||||
iconData,
|
||||
theme,
|
||||
renderVariant,
|
||||
}: IconVariantsSectionProps) {
|
||||
const iconName = theme && iconData.colors?.[theme] ? iconData.colors[theme] : icon
|
||||
return (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2 mb-1">
|
||||
{iconElement}
|
||||
{title}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground mb-4">{description}</p>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
|
||||
{aavailableFormats.map((format) => renderVariant(format, iconName, theme))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type WordmarkSectionProps = {
|
||||
iconData: Icon
|
||||
icon: string
|
||||
aavailableFormats: string[]
|
||||
handleCopy: (url: string, variantKey: string, event?: React.MouseEvent) => void
|
||||
handleDownload: (event: React.MouseEvent, url: string, filename: string) => Promise<void>
|
||||
copiedVariants: Record<string, boolean>
|
||||
renderVariant: RenderVariantFn
|
||||
}
|
||||
|
||||
function WordmarkSection({
|
||||
iconData,
|
||||
aavailableFormats,
|
||||
renderVariant,
|
||||
}: WordmarkSectionProps) {
|
||||
if (!iconData.wordmark) return null
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2 mb-1">
|
||||
<Type className="w-4 h-4 text-green-500" />
|
||||
Wordmark Variants
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground mb-4">
|
||||
Icon variants that include the brand name. Click to copy URL.
|
||||
</p>
|
||||
<div className="space-y-6">
|
||||
{iconData.wordmark.light && (
|
||||
<div>
|
||||
<h4 className="text-md font-medium flex items-center gap-2 mb-3">
|
||||
<Sun className="w-4 h-4 text-amber-500" />
|
||||
Light Theme Wordmark
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
|
||||
{aavailableFormats.map((format) => {
|
||||
if (!iconData.wordmark?.light) return null
|
||||
return renderVariant(format, iconData.wordmark.light, "light")
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{iconData.wordmark.dark && (
|
||||
<div>
|
||||
<h4 className="text-md font-medium flex items-center gap-2 mb-3">
|
||||
<Moon className="w-4 h-4 text-indigo-500" />
|
||||
Dark Theme Wordmark
|
||||
</h4>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
|
||||
{aavailableFormats.map((format) => {
|
||||
if (!iconData.wordmark?.dark) return null
|
||||
return renderVariant(format, iconData.wordmark.dark, "dark")
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export type IconDetailsProps = {
|
||||
icon: string
|
||||
iconData: Icon
|
||||
@@ -30,11 +136,13 @@ export type IconDetailsProps = {
|
||||
export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetailsProps) {
|
||||
const authorName = authorData.name || authorData.login || ""
|
||||
const iconColorVariants = iconData.colors
|
||||
const iconWordmarkVariants = iconData.wordmark
|
||||
const formattedDate = new Date(iconData.update.timestamp).toLocaleDateString("en-GB", {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
})
|
||||
|
||||
const getAvailableFormats = () => {
|
||||
switch (iconData.base) {
|
||||
case "svg":
|
||||
@@ -48,9 +156,12 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
|
||||
const availableFormats = getAvailableFormats()
|
||||
const [copiedVariants, setCopiedVariants] = useState<Record<string, boolean>>({})
|
||||
const [copiedUrlKey, setCopiedUrlKey] = useState<string | null>(null)
|
||||
const [copiedImageKey, setCopiedImageKey] = useState<string | null>(null)
|
||||
|
||||
// Launch confetti from the pointer position
|
||||
const launchConfetti = useCallback((originX?: number, originY?: number) => {
|
||||
if (typeof confetti !== "function") return
|
||||
|
||||
const defaults = {
|
||||
startVelocity: 15,
|
||||
spread: 180,
|
||||
@@ -60,7 +171,6 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
colors: ["#ff0a54", "#ff477e", "#ff7096", "#ff85a1", "#fbb1bd", "#f9bec7"],
|
||||
}
|
||||
|
||||
// If we have origin coordinates, use them
|
||||
if (originX !== undefined && originY !== undefined) {
|
||||
confetti({
|
||||
...defaults,
|
||||
@@ -71,7 +181,6 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
},
|
||||
})
|
||||
} else {
|
||||
// Default to center of screen
|
||||
confetti({
|
||||
...defaults,
|
||||
particleCount: 50,
|
||||
@@ -80,20 +189,13 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleCopy = (url: string, variantKey: string, event?: React.MouseEvent) => {
|
||||
const handleCopyUrl = (url: string, variantKey: string, event?: React.MouseEvent) => {
|
||||
navigator.clipboard.writeText(url)
|
||||
setCopiedVariants((prev) => ({
|
||||
...prev,
|
||||
[variantKey]: true,
|
||||
}))
|
||||
setCopiedUrlKey(variantKey)
|
||||
setTimeout(() => {
|
||||
setCopiedVariants((prev) => ({
|
||||
...prev,
|
||||
[variantKey]: false,
|
||||
}))
|
||||
setCopiedUrlKey(null)
|
||||
}, 2000)
|
||||
|
||||
// Launch confetti from click position or center of screen
|
||||
if (event) {
|
||||
launchConfetti(event.clientX, event.clientY)
|
||||
} else {
|
||||
@@ -105,29 +207,99 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
})
|
||||
}
|
||||
|
||||
const handleCopyImage = async (
|
||||
imageUrl: string,
|
||||
format: string,
|
||||
variantKey: string,
|
||||
event?: React.MouseEvent
|
||||
) => {
|
||||
try {
|
||||
toast.loading("Copying image...")
|
||||
|
||||
if (format === 'svg') {
|
||||
const response = await fetch(imageUrl)
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch SVG: ${response.statusText}`)
|
||||
}
|
||||
const svgText = await response.text()
|
||||
|
||||
await navigator.clipboard.writeText(svgText)
|
||||
|
||||
setCopiedImageKey(variantKey)
|
||||
setTimeout(() => {
|
||||
setCopiedImageKey(null)
|
||||
}, 2000)
|
||||
|
||||
if (event) {
|
||||
launchConfetti(event.clientX, event.clientY)
|
||||
} else {
|
||||
launchConfetti()
|
||||
}
|
||||
|
||||
toast.dismiss()
|
||||
toast.success("SVG Markup Copied", {
|
||||
description: "The SVG code has been copied to your clipboard.",
|
||||
})
|
||||
|
||||
} else if (format === 'png' || format === 'webp') {
|
||||
const mimeType = `image/${format}`
|
||||
const response = await fetch(imageUrl)
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch image: ${response.statusText}`)
|
||||
}
|
||||
const blob = await response.blob()
|
||||
|
||||
if (!blob) {
|
||||
throw new Error('Failed to generate image blob')
|
||||
}
|
||||
|
||||
await navigator.clipboard.write([new ClipboardItem({ [mimeType]: blob })]);
|
||||
|
||||
setCopiedImageKey(variantKey)
|
||||
setTimeout(() => {
|
||||
setCopiedImageKey(null)
|
||||
}, 2000)
|
||||
|
||||
if (event) {
|
||||
launchConfetti(event.clientX, event.clientY)
|
||||
} else {
|
||||
launchConfetti()
|
||||
}
|
||||
|
||||
toast.dismiss()
|
||||
toast.success("Image copied", {
|
||||
description: `The ${format.toUpperCase()} image has been copied to your clipboard.`,
|
||||
})
|
||||
|
||||
} else {
|
||||
throw new Error(`Unsupported format for image copy: ${format}`)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Copy error:", error)
|
||||
toast.dismiss()
|
||||
let description = "Could not copy. Check console for details."
|
||||
if (error instanceof Error) {
|
||||
description = error.message
|
||||
}
|
||||
toast.error("Copy failed", { description })
|
||||
}
|
||||
}
|
||||
|
||||
const handleDownload = async (event: React.MouseEvent, url: string, filename: string) => {
|
||||
event.preventDefault()
|
||||
|
||||
// Launch confetti from download button position
|
||||
launchConfetti(event.clientX, event.clientY)
|
||||
|
||||
try {
|
||||
// Show loading toast
|
||||
toast.loading("Preparing download...")
|
||||
|
||||
// Fetch the file first as a blob
|
||||
const response = await fetch(url)
|
||||
const blob = await response.blob()
|
||||
|
||||
// Create a blob URL and use it for download
|
||||
const blobUrl = URL.createObjectURL(blob)
|
||||
const link = document.createElement("a")
|
||||
link.href = blobUrl
|
||||
link.download = filename
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
|
||||
// Clean up
|
||||
document.body.removeChild(link)
|
||||
setTimeout(() => URL.revokeObjectURL(blobUrl), 100)
|
||||
|
||||
@@ -161,7 +333,7 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
className="relative w-28 h-28 mb-3 cursor-pointer rounded-xl overflow-hidden group"
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
onClick={(e) => handleCopy(imageUrl, variantKey, e)}
|
||||
onClick={(e) => handleCopyUrl(imageUrl, variantKey, e)}
|
||||
aria-label={`Copy ${format.toUpperCase()} URL for ${iconName}${theme ? ` (${theme} theme)` : ""}`}
|
||||
>
|
||||
<div className="absolute inset-0 border-2 border-transparent group-hover:border-primary/20 rounded-xl z-10 transition-colors" />
|
||||
@@ -204,59 +376,18 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
|
||||
<p className="text-sm font-medium">{format.toUpperCase()}</p>
|
||||
|
||||
<div className="flex gap-2 mt-3 w-full justify-center">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-lg cursor-pointer"
|
||||
onClick={(e) => handleDownload(e, imageUrl, `${iconName}.${format}`)}
|
||||
aria-label={`Download ${iconName} in ${format} format${theme ? ` (${theme} theme)` : ""}`}
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Download icon file</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-lg cursor-pointer"
|
||||
onClick={(e) => handleCopy(imageUrl, `btn-${variantKey}`, e)}
|
||||
aria-label={`Copy URL for ${iconName} in ${format} format${theme ? ` (${theme} theme)` : ""}`}
|
||||
>
|
||||
{copiedVariants[`btn-${variantKey}`] ? <Check className="w-4 h-4 text-green-500" /> : <Copy className="w-4 h-4" />}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Copy direct URL to clipboard</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="outline" size="icon" className="h-8 w-8 rounded-lg" asChild>
|
||||
<Link
|
||||
href={githubUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={`View ${iconName} ${format} file on GitHub`}
|
||||
>
|
||||
<Github className="w-4 h-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>View on GitHub</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<IconActions
|
||||
imageUrl={imageUrl}
|
||||
githubUrl={githubUrl}
|
||||
iconName={iconName}
|
||||
format={format}
|
||||
variantKey={variantKey}
|
||||
copiedUrlKey={copiedUrlKey}
|
||||
copiedImageKey={copiedImageKey}
|
||||
handleDownload={handleDownload}
|
||||
handleCopyUrl={handleCopyUrl}
|
||||
handleCopyImage={handleCopyImage}
|
||||
/>
|
||||
</div>
|
||||
</MagicCard>
|
||||
</TooltipProvider>
|
||||
@@ -268,14 +399,13 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
return (
|
||||
<main className="container mx-auto pt-12 pb-14 px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
||||
{/* Left Column: Icon Info and Author */}
|
||||
<div className="lg:col-span-1">
|
||||
<Card className="h-full bg-background/50 border shadow-lg">
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="relative w-32 h-32 rounded-xl overflow-hidden border flex items-center justify-center p-3">
|
||||
<Image
|
||||
src={`${BASE_URL}/${iconData.base}/${icon}.${iconData.base}`}
|
||||
src={`${BASE_URL}/${iconData.base}/${iconData.colors?.light || icon}.${iconData.base}`}
|
||||
width={96}
|
||||
height={96}
|
||||
placeholder="empty"
|
||||
@@ -304,7 +434,7 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
<AvatarImage src={authorData.avatar_url} alt={`${authorName}'s avatar`} />
|
||||
<AvatarFallback>{authorName ? authorName.slice(0, 2).toUpperCase() : "??"}</AvatarFallback>
|
||||
</Avatar>
|
||||
{authorData.html_url ? (
|
||||
{authorData.html_url && (
|
||||
<Link
|
||||
href={authorData.html_url}
|
||||
target="_blank"
|
||||
@@ -313,7 +443,8 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
>
|
||||
{authorName}
|
||||
</Link>
|
||||
) : (
|
||||
)}
|
||||
{!authorData.html_url && (
|
||||
<span className="text-sm">{authorName}</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -370,6 +501,7 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
: `${availableFormats[0].toUpperCase()} format `}
|
||||
with a base format of {iconData.base.toUpperCase()}.
|
||||
{iconData.colors && " Includes both light and dark theme variants for better integration with different UI designs."}
|
||||
{iconData.wordmark && " Wordmark variants are also available for enhanced branding options."}
|
||||
</p>
|
||||
<p>
|
||||
Perfect for adding to dashboards, app directories, documentation, or anywhere you need the {formatIconName(icon)}{" "}
|
||||
@@ -382,7 +514,6 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Middle Column: Icon variants */}
|
||||
<div className="lg:col-span-2">
|
||||
<Card className="h-full bg-background/50 shadow-lg">
|
||||
<CardHeader>
|
||||
@@ -392,37 +523,70 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
<CardDescription>Click on any icon to copy its URL to your clipboard</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{!iconData.colors ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
|
||||
{availableFormats.map((format) => renderVariant(format, icon))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-10">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Sun className="w-4 h-4 text-amber-500" />
|
||||
Light theme
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 p-3 rounded-lg ">
|
||||
{availableFormats.map((format) => renderVariant(format, icon, "light"))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Moon className="w-4 h-4 text-indigo-500" />
|
||||
Dark theme
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 p-3 rounded-lg ">
|
||||
{availableFormats.map((format) => renderVariant(format, icon, "dark"))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-10">
|
||||
{!iconData.colors && (
|
||||
<IconVariantsSection
|
||||
title="Default"
|
||||
description="Standard icon versions. Click to copy URL."
|
||||
iconElement={<FileType className="w-4 h-4 text-blue-500" />}
|
||||
aavailableFormats={availableFormats}
|
||||
icon={icon}
|
||||
iconData={iconData}
|
||||
handleCopy={handleCopyUrl}
|
||||
handleDownload={handleDownload}
|
||||
copiedVariants={copiedVariants}
|
||||
renderVariant={renderVariant}
|
||||
/>
|
||||
)}
|
||||
|
||||
{iconData.colors && (
|
||||
<>
|
||||
<IconVariantsSection
|
||||
title="Light theme"
|
||||
description="Icon variants optimized for light backgrounds (typically lighter icon colors). Click to copy URL."
|
||||
iconElement={<Sun className="w-4 h-4 text-amber-500" />}
|
||||
aavailableFormats={availableFormats}
|
||||
icon={icon}
|
||||
theme="light"
|
||||
iconData={iconData}
|
||||
handleCopy={handleCopyUrl}
|
||||
handleDownload={handleDownload}
|
||||
copiedVariants={copiedVariants}
|
||||
renderVariant={renderVariant}
|
||||
/>
|
||||
|
||||
<IconVariantsSection
|
||||
title="Dark theme"
|
||||
description="Icon variants optimized for dark backgrounds (typically darker icon colors). Click to copy URL."
|
||||
iconElement={<Moon className="w-4 h-4 text-indigo-500" />}
|
||||
aavailableFormats={availableFormats}
|
||||
icon={icon}
|
||||
theme="dark"
|
||||
iconData={iconData}
|
||||
handleCopy={handleCopyUrl}
|
||||
handleDownload={handleDownload}
|
||||
copiedVariants={copiedVariants}
|
||||
renderVariant={renderVariant}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{iconData.wordmark && (
|
||||
<WordmarkSection
|
||||
iconData={iconData}
|
||||
icon={icon}
|
||||
aavailableFormats={availableFormats}
|
||||
handleCopy={handleCopyUrl}
|
||||
handleDownload={handleDownload}
|
||||
copiedVariants={copiedVariants}
|
||||
renderVariant={renderVariant}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Right Column: Technical details */}
|
||||
<div className="lg:col-span-1">
|
||||
<Card className="h-full bg-background/50 border shadow-lg">
|
||||
<CardHeader>
|
||||
@@ -464,6 +628,28 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
</div>
|
||||
)}
|
||||
|
||||
{iconData.wordmark && (
|
||||
<div className="">
|
||||
<h3 className="text-sm font-semibold text-muted-foreground">Wordmark variants</h3>
|
||||
<div className="space-y-2">
|
||||
{iconData.wordmark.light && (
|
||||
<div className="flex items-center gap-2">
|
||||
<Type className="w-4 h-4 text-green-500" />
|
||||
<span className="capitalize font-medium text-sm">Light:</span>
|
||||
<code className="border border-border px-2 py-0.5 rounded-lg text-xs">{iconData.wordmark.light}</code>
|
||||
</div>
|
||||
)}
|
||||
{iconData.wordmark.dark && (
|
||||
<div className="flex items-center gap-2">
|
||||
<Type className="w-4 h-4 text-green-500" />
|
||||
<span className="capitalize font-medium text-sm">Dark:</span>
|
||||
<code className="border border-border px-2 py-0.5 rounded-lg text-xs">{iconData.wordmark.dark}</code>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="">
|
||||
<h3 className="text-sm font-semibold text-muted-foreground mb-2">Source</h3>
|
||||
<Button variant="outline" className="w-full" asChild>
|
||||
|
||||
@@ -11,28 +11,40 @@ import { useState } from "react"
|
||||
export const ISSUE_TEMPLATES = [
|
||||
{
|
||||
id: "add_monochrome_icon",
|
||||
name: "Add light & dark icon",
|
||||
description: "Submit a new icon with both light and dark versions for optimal theme compatibility.",
|
||||
name: "Add Light & Dark Icon",
|
||||
description: "Submit a new icon with distinct light (for dark UIs, e.g., Sonarr) and dark (for light UIs, e.g., Ollama) versions.",
|
||||
url: `${REPO_PATH}/issues/new?template=add_monochrome_icon.yml`,
|
||||
},
|
||||
{
|
||||
id: "add_normal_icon",
|
||||
name: "Add normal icon",
|
||||
description: "Submit a new icon that works well across both light and dark themes.",
|
||||
name: "Add Normal Icon",
|
||||
description: "Submit a new icon designed to be versatile across both light and dark themes without separate versions.",
|
||||
url: `${REPO_PATH}/issues/new?template=add_normal_icon.yml`,
|
||||
},
|
||||
{
|
||||
id: "add_wordmark_icon",
|
||||
name: "Add Wordmark Icon",
|
||||
description: "Submit a new wordmark icon (icon with brand name). Can be single-version or have light/dark variants.",
|
||||
url: `${REPO_PATH}/issues/new?template=add_wordmark_icon.yml`,
|
||||
},
|
||||
{
|
||||
id: "update_monochrome_icon",
|
||||
name: "Update light & dark icon",
|
||||
description: "Improve an existing icon by updating both light and dark versions.",
|
||||
name: "Update Light & Dark Icon",
|
||||
description: "Improve an existing icon with light and dark versions, clarifying light (e.g., Sonarr) vs. dark (e.g., Ollama) variants.",
|
||||
url: `${REPO_PATH}/issues/new?template=update_monochrome_icon.yml`,
|
||||
},
|
||||
{
|
||||
id: "update_normal_icon",
|
||||
name: "Update normal icon",
|
||||
description: "Improve an existing icon that works across both light and dark themes.",
|
||||
name: "Update Normal Icon",
|
||||
description: "Improve an existing versatile icon that works across both light and dark themes.",
|
||||
url: `${REPO_PATH}/issues/new?template=update_normal_icon.yml`,
|
||||
},
|
||||
{
|
||||
id: "update_wordmark_icon",
|
||||
name: "Update Wordmark Icon",
|
||||
description: "Improve an existing wordmark icon (icon with brand name).",
|
||||
url: `${REPO_PATH}/issues/new?template=update_wordmark_icon.yml`,
|
||||
},
|
||||
{
|
||||
id: "blank_issue",
|
||||
name: "Something else",
|
||||
|
||||
@@ -13,12 +13,18 @@ export type IconColors = {
|
||||
light?: string
|
||||
}
|
||||
|
||||
export type IconWordmarkColors = {
|
||||
dark?: string
|
||||
light?: string
|
||||
}
|
||||
|
||||
export type Icon = {
|
||||
base: string | "svg" | "png" | "webp"
|
||||
aliases: string[]
|
||||
categories: string[]
|
||||
update: IconUpdate
|
||||
colors?: IconColors
|
||||
wordmark?: IconWordmarkColors
|
||||
}
|
||||
|
||||
export type IconFile = {
|
||||
|
||||
Reference in New Issue
Block a user