refactor: update variable names and comments
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,14 +4,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is **@invisi/flaticon-uicons**, a local web-compatible icon font package with 50,492+ icons across 15 style variations (stroke weights × corner styles). Icons are sourced from Flaticon's public API and converted to webfonts using FontForge.
|
||||
This is **@invisi/ficons**, a local web-compatible icon font package with thousands of icons across 15 style variations (stroke weights × corner styles). Icons are converted to webfonts using FontForge.
|
||||
|
||||
The output is a drop-in CSS font system - users link `fonts/flaticon.css` and use class names like `fi-rs-bookmark` to display icons.
|
||||
The output is a drop-in CSS font system - users link `fonts/ficons.css` and use class names like `fi-rs-bookmark` to display icons.
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Fetch latest icon metadata from Flaticon API (writes to data/all_icons.json)
|
||||
# Fetch latest icon metadata (writes to data/all_icons.json)
|
||||
npm run update:icons
|
||||
# or: node update-icon-list.js
|
||||
|
||||
@@ -44,9 +44,9 @@ Examples: `fi-rs-bookmark`, `fi-br-home`, `fi-brands-instagram`
|
||||
|
||||
### Data Pipeline
|
||||
|
||||
1. **`update-icon-list.js`** - Fetches icon metadata from Flaticon API with pagination (117 pages). Rate-limited (100ms delay). Outputs `data/all_icons.json` (~53MB).
|
||||
1. **`update-icon-list.js`** - Fetches icon metadata with pagination. Rate-limited (100ms delay). Outputs `data/all_icons.json`.
|
||||
|
||||
2. **`build-icons-js.js`** - Converts JSON to `window.FLATICON_ICONS` global for browser use in explorer (~5.7MB).
|
||||
2. **`build-icons-js.js`** - Converts JSON to `window.FICONS_DATA` global for browser use in explorer.
|
||||
|
||||
3. **`build-fonts.js`** - Node.js orchestrator that calls FontForge via `scripts/build-font.py` to convert SVGs → TTF → WOFF/WOFF2. Generates CSS with `@font-face` rules and content-based icon classes.
|
||||
|
||||
@@ -62,7 +62,7 @@ Examples: `fi-rs-bookmark`, `fi-br-home`, `fi-brands-instagram`
|
||||
|
||||
### Key Files
|
||||
|
||||
- `fonts/flaticon.css` - Unified CSS importing all font-face definitions
|
||||
- `fonts/ficons.css` - Unified CSS importing all font-face definitions
|
||||
- `explorer.html` + `explorer.js` + `explorer.css` - Interactive icon browser with search/filter
|
||||
- `index.js` - Module exports for npm distribution
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
# [@invisi/flaticon-uicons](https://www.flaticon.com/uicons/interface-icons)
|
||||
# @invisi/ficons
|
||||
|
||||
A local web-compatible icon font package with **50,000+ icons** across multiple style variations. Icons are sourced from Flaticon's public API and converted to webfonts using FontForge.
|
||||
|
||||

|
||||
A local web-compatible icon font package with **thousands of icons** across multiple style variations.
|
||||
|
||||
## Features
|
||||
|
||||
- **50,492+ icons** across 15 style variations (stroke weights × corner styles)
|
||||
- **Thousands of icons** across 15 style variations (stroke weights × corner styles)
|
||||
- **Self-hosted** - no CDN dependencies
|
||||
- **Interactive explorer** - browse, search, and filter icons locally
|
||||
- **Web fonts** - WOFF2, WOFF, TTF formats
|
||||
@@ -15,7 +13,7 @@ A local web-compatible icon font package with **50,000+ icons** across multiple
|
||||
## Installation
|
||||
|
||||
```shell
|
||||
npm i @invisi/flaticon-uicons
|
||||
npm i @invisi/ficons
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
@@ -23,7 +21,7 @@ npm i @invisi/flaticon-uicons
|
||||
Include the CSS in your HTML:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="fonts/flaticon.css">
|
||||
<link rel="stylesheet" href="fonts/ficons.css">
|
||||
```
|
||||
|
||||
Use icons with `<span>` or `<i>` elements:
|
||||
@@ -68,7 +66,7 @@ Use icons with `<span>` or `<i>` elements:
|
||||
| Duotone Chubby | 270 |
|
||||
| Brands | 245 |
|
||||
|
||||
**Total: 50,492 icon variations**
|
||||
**Thousands of icon variations**
|
||||
|
||||
## Styling
|
||||
|
||||
@@ -88,7 +86,7 @@ Icons inherit `font-size` and `color` from their parent:
|
||||
|
||||
```
|
||||
fonts/
|
||||
flaticon.css # Unified CSS (imports all styles)
|
||||
ficons.css # Unified CSS (imports all styles)
|
||||
css/
|
||||
flaticon-regular-straight.css
|
||||
flaticon-regular-rounded.css
|
||||
@@ -106,14 +104,14 @@ Search for icons directly from the command line using `npx`:
|
||||
|
||||
```bash
|
||||
# Basic search
|
||||
npx @invisi/flaticon-uicons camera
|
||||
npx @invisi/ficons camera
|
||||
|
||||
# Search with multiple keywords
|
||||
npx @invisi/flaticon-uicons arrow left
|
||||
npx @invisi/ficons arrow left
|
||||
|
||||
# Filter by variation (prefix)
|
||||
npx @invisi/flaticon-uicons user rr
|
||||
npx @invisi/flaticon-uicons home --variation ss
|
||||
npx @invisi/ficons user rr
|
||||
npx @invisi/ficons home --variation ss
|
||||
```
|
||||
|
||||
Outputs JSON with icon names and available variations:
|
||||
@@ -150,7 +148,7 @@ Outputs JSON with icon names and available variations:
|
||||
|
||||
Open `explorer.html` in a browser to:
|
||||
|
||||
- Browse all 50,000+ icons
|
||||
- Browse all icons
|
||||
- Search by name or tags
|
||||
- Filter by weight, corner style, and type
|
||||
- Copy HTML snippets and CSS classes
|
||||
@@ -159,7 +157,7 @@ Open `explorer.html` in a browser to:
|
||||
## Development Scripts
|
||||
|
||||
```bash
|
||||
# Fetch latest icon metadata from Flaticon API
|
||||
# Fetch latest icon metadata
|
||||
npm run update:icons
|
||||
# or: node update-icon-list.js
|
||||
|
||||
@@ -171,13 +169,3 @@ npm run build:icons
|
||||
npm run build:fonts
|
||||
# or: node build-fonts.js
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Icons sourced from [Flaticon UIcons](https://www.flaticon.com/uicons). Please refer to [Flaticon's license](https://www.flaticon.com/legal) for usage terms.
|
||||
|
||||
## Attribution
|
||||
|
||||
```
|
||||
Uicons by <a href="https://www.flaticon.com/uicons">Flaticon</a>
|
||||
```
|
||||
|
||||
+11
-11
@@ -60,7 +60,7 @@ function buildCss({ order, mapping, outputPath }) {
|
||||
// Generate base.css with shared styles
|
||||
const baseLines = [];
|
||||
baseLines.push('/*!');
|
||||
baseLines.push(' * Flaticon Icon Fonts - Base Styles');
|
||||
baseLines.push(' * Ficons Icon Fonts - Base Styles');
|
||||
baseLines.push(' * Required for all icon fonts to render correctly');
|
||||
baseLines.push(' */');
|
||||
baseLines.push('');
|
||||
@@ -99,20 +99,20 @@ function buildCss({ order, mapping, outputPath }) {
|
||||
const lines = [];
|
||||
lines.push('/*!');
|
||||
lines.push(` * ${label} (${prefix})`);
|
||||
lines.push(' * Generated from Flaticon icon API');
|
||||
lines.push(' * Icon font generated from SVG sources');
|
||||
lines.push(' */');
|
||||
lines.push('@import url("./base.css");');
|
||||
lines.push('@font-face {');
|
||||
lines.push(` font-family: "flaticon-${fileSuffix}";`);
|
||||
lines.push(` src: url("../webfonts/flaticon-${fileSuffix}.woff2") format("woff2"),`);
|
||||
lines.push(` url("../webfonts/flaticon-${fileSuffix}.woff") format("woff"),`);
|
||||
lines.push(` url("../webfonts/flaticon-${fileSuffix}.ttf") format("truetype");`);
|
||||
lines.push(` font-family: "ficons-${fileSuffix}";`);
|
||||
lines.push(` src: url("../webfonts/ficons-${fileSuffix}.woff2") format("woff2"),`);
|
||||
lines.push(` url("../webfonts/ficons-${fileSuffix}.woff") format("woff"),`);
|
||||
lines.push(` url("../webfonts/ficons-${fileSuffix}.ttf") format("truetype");`);
|
||||
lines.push(' font-display: swap;');
|
||||
lines.push('}');
|
||||
lines.push('');
|
||||
lines.push(`i[class^="fi-${prefix}-"]:before, i[class*=" fi-${prefix}-"]:before,`);
|
||||
lines.push(`span[class^="fi-${prefix}-"]:before, span[class*=" fi-${prefix}-"]:before {`);
|
||||
lines.push(` font-family: flaticon-${fileSuffix} !important;`);
|
||||
lines.push(` font-family: ficons-${fileSuffix} !important;`);
|
||||
lines.push(' font-style: normal;');
|
||||
lines.push(' font-weight: normal !important;');
|
||||
lines.push('}');
|
||||
@@ -148,11 +148,11 @@ function buildCss({ order, mapping, outputPath }) {
|
||||
fs.writeFileSync(path.join(cssDir, `${stroke}.css`), groupLines.join('\n'));
|
||||
});
|
||||
|
||||
// Generate all.css (main entry point)
|
||||
// Generate ficons.css (main entry point)
|
||||
const allLines = [];
|
||||
allLines.push('/*!');
|
||||
allLines.push(' * Flaticon Icon Fonts - All Icons');
|
||||
allLines.push(' * Generated from Flaticon icon API');
|
||||
allLines.push(' * Ficons Icon Fonts - All Icons');
|
||||
allLines.push(' * Icon fonts generated from SVG sources');
|
||||
allLines.push(' */');
|
||||
allLines.push('');
|
||||
allLines.push('@import url("./css/base.css");');
|
||||
@@ -204,7 +204,7 @@ function main() {
|
||||
const prefixes = args.prefix ? String(args.prefix).split(',') : Object.keys(PREFIX_CONFIG);
|
||||
const clean = Boolean(args.clean);
|
||||
const outputDir = args.outputDir || 'fonts/webfonts';
|
||||
const cssPath = args.css || 'fonts/all.css';
|
||||
const cssPath = args.css || 'fonts/ficons.css';
|
||||
|
||||
const iconsByPrefix = loadIcons();
|
||||
const buildOrder = Object.keys(PREFIX_CONFIG).filter(prefix => prefixes.includes(prefix));
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ function buildIconsJs({
|
||||
is_brand
|
||||
}));
|
||||
|
||||
const payload = `window.FLATICON_ICONS = ${JSON.stringify(icons)};\n`;
|
||||
const payload = `window.FICONS_DATA = ${JSON.stringify(icons)};\n`;
|
||||
fs.writeFileSync(outputPath, payload, 'utf8');
|
||||
|
||||
const sizeMb = (payload.length / (1024 * 1024)).toFixed(1);
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+3
-3
@@ -3,8 +3,8 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Flaticon Explorer</title>
|
||||
<link rel="stylesheet" href="fonts/all.css">
|
||||
<title>Ficons Explorer</title>
|
||||
<link rel="stylesheet" href="fonts/ficons.css">
|
||||
<link rel="stylesheet" href="explorer.css">
|
||||
<script>try{document.documentElement.dataset.theme=localStorage.getItem('theme')??(matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light')}catch{}</script>
|
||||
</head>
|
||||
@@ -17,7 +17,7 @@
|
||||
<span class="fi fi-rs-sparkles"></span>
|
||||
</div>
|
||||
<div>
|
||||
<h1>Flaticon Explorer</h1>
|
||||
<h1>Ficons Explorer</h1>
|
||||
<div class="header-subtitle">Just a little place to find nice icons</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+2
-2
@@ -70,8 +70,8 @@ function getCurrentPrefix() {
|
||||
|
||||
async function loadIcons() {
|
||||
try {
|
||||
if (Array.isArray(window.FLATICON_ICONS) && window.FLATICON_ICONS.length) {
|
||||
allIcons = window.FLATICON_ICONS;
|
||||
if (Array.isArray(window.FICONS_DATA) && window.FICONS_DATA.length) {
|
||||
allIcons = window.FICONS_DATA;
|
||||
} else {
|
||||
const response = await fetch('data/all_icons.json');
|
||||
if (!response.ok) throw new Error('Failed to load icons');
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Flaticon Icon Fonts - Base Styles
|
||||
* Ficons Icon Fonts - Base Styles
|
||||
* Required for all icon fonts to render correctly
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Bold Rounded (br)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-bold-rounded";
|
||||
src: url("../webfonts/flaticon-bold-rounded.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-bold-rounded.woff") format("woff"),
|
||||
url("../webfonts/flaticon-bold-rounded.ttf") format("truetype");
|
||||
font-family: "ficons-bold-rounded";
|
||||
src: url("../webfonts/ficons-bold-rounded.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-bold-rounded.woff") format("woff"),
|
||||
url("../webfonts/ficons-bold-rounded.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-br-"]:before, i[class*=" fi-br-"]:before,
|
||||
span[class^="fi-br-"]:before, span[class*=" fi-br-"]:before {
|
||||
font-family: flaticon-bold-rounded !important;
|
||||
font-family: ficons-bold-rounded !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Bold Straight (bs)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-bold-straight";
|
||||
src: url("../webfonts/flaticon-bold-straight.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-bold-straight.woff") format("woff"),
|
||||
url("../webfonts/flaticon-bold-straight.ttf") format("truetype");
|
||||
font-family: "ficons-bold-straight";
|
||||
src: url("../webfonts/ficons-bold-straight.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-bold-straight.woff") format("woff"),
|
||||
url("../webfonts/ficons-bold-straight.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-bs-"]:before, i[class*=" fi-bs-"]:before,
|
||||
span[class^="fi-bs-"]:before, span[class*=" fi-bs-"]:before {
|
||||
font-family: flaticon-bold-straight !important;
|
||||
font-family: ficons-bold-straight !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Brands (brands)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-brands";
|
||||
src: url("../webfonts/flaticon-brands.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-brands.woff") format("woff"),
|
||||
url("../webfonts/flaticon-brands.ttf") format("truetype");
|
||||
font-family: "ficons-brands";
|
||||
src: url("../webfonts/ficons-brands.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-brands.woff") format("woff"),
|
||||
url("../webfonts/ficons-brands.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-brands-"]:before, i[class*=" fi-brands-"]:before,
|
||||
span[class^="fi-brands-"]:before, span[class*=" fi-brands-"]:before {
|
||||
font-family: flaticon-brands !important;
|
||||
font-family: ficons-brands !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Duotone Chubby (dc)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-duotone-chubby";
|
||||
src: url("../webfonts/flaticon-duotone-chubby.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-duotone-chubby.woff") format("woff"),
|
||||
url("../webfonts/flaticon-duotone-chubby.ttf") format("truetype");
|
||||
font-family: "ficons-duotone-chubby";
|
||||
src: url("../webfonts/ficons-duotone-chubby.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-duotone-chubby.woff") format("woff"),
|
||||
url("../webfonts/ficons-duotone-chubby.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-dc-"]:before, i[class*=" fi-dc-"]:before,
|
||||
span[class^="fi-dc-"]:before, span[class*=" fi-dc-"]:before {
|
||||
font-family: flaticon-duotone-chubby !important;
|
||||
font-family: ficons-duotone-chubby !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Duotone Rounded (dr)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-duotone-rounded";
|
||||
src: url("../webfonts/flaticon-duotone-rounded.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-duotone-rounded.woff") format("woff"),
|
||||
url("../webfonts/flaticon-duotone-rounded.ttf") format("truetype");
|
||||
font-family: "ficons-duotone-rounded";
|
||||
src: url("../webfonts/ficons-duotone-rounded.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-duotone-rounded.woff") format("woff"),
|
||||
url("../webfonts/ficons-duotone-rounded.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-dr-"]:before, i[class*=" fi-dr-"]:before,
|
||||
span[class^="fi-dr-"]:before, span[class*=" fi-dr-"]:before {
|
||||
font-family: flaticon-duotone-rounded !important;
|
||||
font-family: ficons-duotone-rounded !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Duotone Straight (ds)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-duotone-straight";
|
||||
src: url("../webfonts/flaticon-duotone-straight.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-duotone-straight.woff") format("woff"),
|
||||
url("../webfonts/flaticon-duotone-straight.ttf") format("truetype");
|
||||
font-family: "ficons-duotone-straight";
|
||||
src: url("../webfonts/ficons-duotone-straight.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-duotone-straight.woff") format("woff"),
|
||||
url("../webfonts/ficons-duotone-straight.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-ds-"]:before, i[class*=" fi-ds-"]:before,
|
||||
span[class^="fi-ds-"]:before, span[class*=" fi-ds-"]:before {
|
||||
font-family: flaticon-duotone-straight !important;
|
||||
font-family: ficons-duotone-straight !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Regular Chubby (rc)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-regular-chubby";
|
||||
src: url("../webfonts/flaticon-regular-chubby.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-regular-chubby.woff") format("woff"),
|
||||
url("../webfonts/flaticon-regular-chubby.ttf") format("truetype");
|
||||
font-family: "ficons-regular-chubby";
|
||||
src: url("../webfonts/ficons-regular-chubby.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-regular-chubby.woff") format("woff"),
|
||||
url("../webfonts/ficons-regular-chubby.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-rc-"]:before, i[class*=" fi-rc-"]:before,
|
||||
span[class^="fi-rc-"]:before, span[class*=" fi-rc-"]:before {
|
||||
font-family: flaticon-regular-chubby !important;
|
||||
font-family: ficons-regular-chubby !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Regular Rounded (rr)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-regular-rounded";
|
||||
src: url("../webfonts/flaticon-regular-rounded.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-regular-rounded.woff") format("woff"),
|
||||
url("../webfonts/flaticon-regular-rounded.ttf") format("truetype");
|
||||
font-family: "ficons-regular-rounded";
|
||||
src: url("../webfonts/ficons-regular-rounded.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-regular-rounded.woff") format("woff"),
|
||||
url("../webfonts/ficons-regular-rounded.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-rr-"]:before, i[class*=" fi-rr-"]:before,
|
||||
span[class^="fi-rr-"]:before, span[class*=" fi-rr-"]:before {
|
||||
font-family: flaticon-regular-rounded !important;
|
||||
font-family: ficons-regular-rounded !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Regular Straight (rs)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-regular-straight";
|
||||
src: url("../webfonts/flaticon-regular-straight.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-regular-straight.woff") format("woff"),
|
||||
url("../webfonts/flaticon-regular-straight.ttf") format("truetype");
|
||||
font-family: "ficons-regular-straight";
|
||||
src: url("../webfonts/ficons-regular-straight.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-regular-straight.woff") format("woff"),
|
||||
url("../webfonts/ficons-regular-straight.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-rs-"]:before, i[class*=" fi-rs-"]:before,
|
||||
span[class^="fi-rs-"]:before, span[class*=" fi-rs-"]:before {
|
||||
font-family: flaticon-regular-straight !important;
|
||||
font-family: ficons-regular-straight !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Solid Chubby (sc)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-solid-chubby";
|
||||
src: url("../webfonts/flaticon-solid-chubby.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-solid-chubby.woff") format("woff"),
|
||||
url("../webfonts/flaticon-solid-chubby.ttf") format("truetype");
|
||||
font-family: "ficons-solid-chubby";
|
||||
src: url("../webfonts/ficons-solid-chubby.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-solid-chubby.woff") format("woff"),
|
||||
url("../webfonts/ficons-solid-chubby.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-sc-"]:before, i[class*=" fi-sc-"]:before,
|
||||
span[class^="fi-sc-"]:before, span[class*=" fi-sc-"]:before {
|
||||
font-family: flaticon-solid-chubby !important;
|
||||
font-family: ficons-solid-chubby !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Solid Rounded (sr)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-solid-rounded";
|
||||
src: url("../webfonts/flaticon-solid-rounded.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-solid-rounded.woff") format("woff"),
|
||||
url("../webfonts/flaticon-solid-rounded.ttf") format("truetype");
|
||||
font-family: "ficons-solid-rounded";
|
||||
src: url("../webfonts/ficons-solid-rounded.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-solid-rounded.woff") format("woff"),
|
||||
url("../webfonts/ficons-solid-rounded.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-sr-"]:before, i[class*=" fi-sr-"]:before,
|
||||
span[class^="fi-sr-"]:before, span[class*=" fi-sr-"]:before {
|
||||
font-family: flaticon-solid-rounded !important;
|
||||
font-family: ficons-solid-rounded !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Solid Straight (ss)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-solid-straight";
|
||||
src: url("../webfonts/flaticon-solid-straight.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-solid-straight.woff") format("woff"),
|
||||
url("../webfonts/flaticon-solid-straight.ttf") format("truetype");
|
||||
font-family: "ficons-solid-straight";
|
||||
src: url("../webfonts/ficons-solid-straight.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-solid-straight.woff") format("woff"),
|
||||
url("../webfonts/ficons-solid-straight.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-ss-"]:before, i[class*=" fi-ss-"]:before,
|
||||
span[class^="fi-ss-"]:before, span[class*=" fi-ss-"]:before {
|
||||
font-family: flaticon-solid-straight !important;
|
||||
font-family: ficons-solid-straight !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Thin Chubby (tc)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-thin-chubby";
|
||||
src: url("../webfonts/flaticon-thin-chubby.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-thin-chubby.woff") format("woff"),
|
||||
url("../webfonts/flaticon-thin-chubby.ttf") format("truetype");
|
||||
font-family: "ficons-thin-chubby";
|
||||
src: url("../webfonts/ficons-thin-chubby.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-thin-chubby.woff") format("woff"),
|
||||
url("../webfonts/ficons-thin-chubby.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-tc-"]:before, i[class*=" fi-tc-"]:before,
|
||||
span[class^="fi-tc-"]:before, span[class*=" fi-tc-"]:before {
|
||||
font-family: flaticon-thin-chubby !important;
|
||||
font-family: ficons-thin-chubby !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Thin Rounded (tr)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-thin-rounded";
|
||||
src: url("../webfonts/flaticon-thin-rounded.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-thin-rounded.woff") format("woff"),
|
||||
url("../webfonts/flaticon-thin-rounded.ttf") format("truetype");
|
||||
font-family: "ficons-thin-rounded";
|
||||
src: url("../webfonts/ficons-thin-rounded.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-thin-rounded.woff") format("woff"),
|
||||
url("../webfonts/ficons-thin-rounded.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-tr-"]:before, i[class*=" fi-tr-"]:before,
|
||||
span[class^="fi-tr-"]:before, span[class*=" fi-tr-"]:before {
|
||||
font-family: flaticon-thin-rounded !important;
|
||||
font-family: ficons-thin-rounded !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*!
|
||||
* Thin Straight (ts)
|
||||
* Generated from Flaticon icon API
|
||||
* Icon font generated from SVG sources
|
||||
*/
|
||||
@import url("./base.css");
|
||||
@font-face {
|
||||
font-family: "flaticon-thin-straight";
|
||||
src: url("../webfonts/flaticon-thin-straight.woff2") format("woff2"),
|
||||
url("../webfonts/flaticon-thin-straight.woff") format("woff"),
|
||||
url("../webfonts/flaticon-thin-straight.ttf") format("truetype");
|
||||
font-family: "ficons-thin-straight";
|
||||
src: url("../webfonts/ficons-thin-straight.woff2") format("woff2"),
|
||||
url("../webfonts/ficons-thin-straight.woff") format("woff"),
|
||||
url("../webfonts/ficons-thin-straight.ttf") format("truetype");
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
i[class^="fi-ts-"]:before, i[class*=" fi-ts-"]:before,
|
||||
span[class^="fi-ts-"]:before, span[class*=" fi-ts-"]:before {
|
||||
font-family: flaticon-thin-straight !important;
|
||||
font-family: ficons-thin-straight !important;
|
||||
font-style: normal;
|
||||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Flaticon Icon Fonts - All Icons
|
||||
* Generated from Flaticon icon API
|
||||
* Ficons Icon Fonts - All Icons
|
||||
* Icon fonts generated from SVG sources
|
||||
*/
|
||||
|
||||
@import url("./css/base.css");
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,119 +1,354 @@
|
||||
#!/usr/bin/env node
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const vm = require('vm');
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
const http = require('node:http');
|
||||
const url = require('node:url');
|
||||
const { parseArgs } = require('node:util');
|
||||
|
||||
const paths = {
|
||||
css: path.join(__dirname, 'fonts', 'flaticon.css'),
|
||||
webfonts: path.join(__dirname, 'fonts', 'webfonts'),
|
||||
explorer: path.join(__dirname, 'explorer.html'),
|
||||
iconData: path.join(__dirname, 'data', 'all_icons.js')
|
||||
// ============================================================================
|
||||
// CONSTANTS
|
||||
// ============================================================================
|
||||
|
||||
const CONFIG = {
|
||||
host: '127.0.0.1',
|
||||
port: 12667,
|
||||
paths: {
|
||||
css: path.join(__dirname, 'fonts', 'ficons.css'),
|
||||
webfonts: path.join(__dirname, 'fonts', 'webfonts'),
|
||||
explorer: path.join(__dirname, 'explorer.html'),
|
||||
iconData: path.join(__dirname, 'data', 'all_icons.js')
|
||||
}
|
||||
};
|
||||
|
||||
function loadIcons() {
|
||||
const source = fs.readFileSync(paths.iconData, 'utf8');
|
||||
const sandbox = { window: {} };
|
||||
vm.createContext(sandbox);
|
||||
vm.runInContext(source, sandbox);
|
||||
const icons = sandbox.window.FLATICON_ICONS;
|
||||
if (!Array.isArray(icons)) {
|
||||
throw new Error('Icon data was not loaded correctly.');
|
||||
}
|
||||
return icons;
|
||||
}
|
||||
const MIME_TYPES = {
|
||||
'.html': 'text/html',
|
||||
'.js': 'text/javascript',
|
||||
'.css': 'text/css',
|
||||
'.json': 'application/json',
|
||||
'.woff': 'font/woff',
|
||||
'.woff2': 'font/woff2',
|
||||
'.ttf': 'font/ttf',
|
||||
};
|
||||
|
||||
function matchIcon(icon, terms) {
|
||||
const haystack = `${icon.name} ${icon.tags || ''}`.toLowerCase();
|
||||
return terms.every((term) => haystack.includes(term));
|
||||
}
|
||||
const COMMANDS = {
|
||||
EXPLORE: 'explore',
|
||||
SEARCH: 'search'
|
||||
};
|
||||
|
||||
function formatMatchesJson(matches) {
|
||||
const grouped = new Map();
|
||||
matches.forEach((icon) => {
|
||||
if (!grouped.has(icon.name)) grouped.set(icon.name, new Set());
|
||||
grouped.get(icon.name).add(icon.prefix);
|
||||
});
|
||||
const EXIT_CODES = {
|
||||
SUCCESS: 0,
|
||||
ERROR: 1
|
||||
};
|
||||
|
||||
if (grouped.size === 0) {
|
||||
return '{}';
|
||||
// ============================================================================
|
||||
// ICON DATA LAYER
|
||||
// ============================================================================
|
||||
|
||||
class IconLoader {
|
||||
constructor(dataPath) {
|
||||
this.dataPath = dataPath;
|
||||
this._cache = null;
|
||||
}
|
||||
|
||||
const lines = [];
|
||||
grouped.forEach((set, name) => {
|
||||
const variations = Array.from(set).sort();
|
||||
const variationsJson = `[${variations.map((v) => JSON.stringify(v)).join(', ')}]`;
|
||||
lines.push(` ${JSON.stringify(name)}: ${variationsJson}`);
|
||||
});
|
||||
load() {
|
||||
if (this._cache) {
|
||||
return this._cache;
|
||||
}
|
||||
|
||||
return `{\n${lines.join(',\n')}\n}`;
|
||||
}
|
||||
const source = fs.readFileSync(this.dataPath, 'utf8');
|
||||
const sandbox = { window: {} };
|
||||
vm.createContext(sandbox);
|
||||
vm.runInContext(source, sandbox);
|
||||
|
||||
function printHelp() {
|
||||
console.log('Usage: npx <this-package> <keyword...> [variation]');
|
||||
console.log(' or: npx <this-package> <keyword...> --variation <prefix>');
|
||||
console.log('Example: npx <this-package> camera');
|
||||
console.log('Example: npx <this-package> arrow rr');
|
||||
console.log('Outputs: JSON object {"icon-name": ["<prefix>", ...]}');
|
||||
}
|
||||
const icons = sandbox.window.FICONS_DATA;
|
||||
if (!Array.isArray(icons)) {
|
||||
throw new Error('Icon data was not loaded correctly.');
|
||||
}
|
||||
|
||||
function runCli() {
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length === 0 || args.includes('-h') || args.includes('--help')) {
|
||||
printHelp();
|
||||
process.exit(args.length === 0 ? 1 : 0);
|
||||
this._cache = icons;
|
||||
return icons;
|
||||
}
|
||||
|
||||
let icons;
|
||||
try {
|
||||
icons = loadIcons();
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
process.exit(1);
|
||||
getPrefixes() {
|
||||
return new Set(this.load().map(icon => icon.prefix));
|
||||
}
|
||||
}
|
||||
|
||||
class IconSearcher {
|
||||
constructor(icons) {
|
||||
this.icons = icons;
|
||||
}
|
||||
|
||||
let variation = null;
|
||||
const filteredArgs = [];
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
const arg = args[i];
|
||||
if (arg === '-v' || arg === '--variation' || arg === '--variant') {
|
||||
variation = args[i + 1];
|
||||
if (!variation) {
|
||||
console.error('Missing variation prefix after --variation.');
|
||||
process.exit(1);
|
||||
search(query, variation = null) {
|
||||
const terms = this._parseQuery(query);
|
||||
return this.icons.filter(icon =>
|
||||
this._matches(icon, terms) && this._matchesVariation(icon, variation)
|
||||
);
|
||||
}
|
||||
|
||||
_parseQuery(query) {
|
||||
return query.toLowerCase().split(/\s+/).filter(Boolean);
|
||||
}
|
||||
|
||||
_matches(icon, terms) {
|
||||
const haystack = `${icon.name} ${icon.tags || ''}`.toLowerCase();
|
||||
return terms.every(term => haystack.includes(term));
|
||||
}
|
||||
|
||||
_matchesVariation(icon, variation) {
|
||||
return !variation || icon.prefix === variation;
|
||||
}
|
||||
}
|
||||
|
||||
class ResultFormatter {
|
||||
static formatJson(matches) {
|
||||
const grouped = this._groupByIconName(matches);
|
||||
|
||||
if (grouped.size === 0) {
|
||||
return '{}';
|
||||
}
|
||||
|
||||
const entries = Array.from(grouped.entries())
|
||||
.map(([name, variations]) => {
|
||||
const sortedVariations = Array.from(variations).sort();
|
||||
const variationsJson = JSON.stringify(sortedVariations);
|
||||
return ` ${JSON.stringify(name)}: ${variationsJson}`;
|
||||
});
|
||||
|
||||
return `{\n${entries.join(',\n')}\n}`;
|
||||
}
|
||||
|
||||
static _groupByIconName(matches) {
|
||||
const grouped = new Map();
|
||||
for (const icon of matches) {
|
||||
if (!grouped.has(icon.name)) {
|
||||
grouped.set(icon.name, new Set());
|
||||
}
|
||||
i += 1;
|
||||
continue;
|
||||
grouped.get(icon.name).add(icon.prefix);
|
||||
}
|
||||
filteredArgs.push(arg);
|
||||
return grouped;
|
||||
}
|
||||
|
||||
if (!variation && filteredArgs.length > 1) {
|
||||
const prefixes = new Set(icons.map((icon) => icon.prefix));
|
||||
const last = filteredArgs[filteredArgs.length - 1].toLowerCase();
|
||||
if (prefixes.has(last)) {
|
||||
variation = last;
|
||||
filteredArgs.pop();
|
||||
}
|
||||
}
|
||||
|
||||
const query = filteredArgs.join(' ').trim();
|
||||
if (!query) {
|
||||
printHelp();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
||||
if (variation) variation = variation.toLowerCase();
|
||||
|
||||
const matches = icons.filter(
|
||||
(icon) => matchIcon(icon, terms) && (!variation || icon.prefix === variation)
|
||||
);
|
||||
if (matches.length === 0) {
|
||||
console.log('{}');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(formatMatchesJson(matches));
|
||||
}
|
||||
|
||||
runCli();
|
||||
// ============================================================================
|
||||
// HTTP SERVER LAYER
|
||||
// ============================================================================
|
||||
|
||||
class FileServer {
|
||||
constructor(rootDir) {
|
||||
this.rootDir = rootDir;
|
||||
}
|
||||
|
||||
serve(req, res) {
|
||||
const requestPath = this._sanitizePath(req.url);
|
||||
const fullPath = path.join(this.rootDir, requestPath);
|
||||
|
||||
this._serveFile(fullPath, req, res);
|
||||
}
|
||||
|
||||
_sanitizePath(urlPath) {
|
||||
const parsed = url.parse(urlPath);
|
||||
let pathname = parsed.pathname;
|
||||
|
||||
if (pathname === '/') {
|
||||
return '/explorer.html';
|
||||
}
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
_serveFile(filepath, req, res) {
|
||||
fs.stat(filepath, (err, stats) => {
|
||||
if (err || !stats.isFile()) {
|
||||
this._send404(res);
|
||||
return;
|
||||
}
|
||||
this._sendFile(filepath, res);
|
||||
});
|
||||
}
|
||||
|
||||
_sendFile(filepath, res) {
|
||||
fs.readFile(filepath, (err, data) => {
|
||||
if (err) {
|
||||
this._send404(res);
|
||||
return;
|
||||
}
|
||||
|
||||
const mimeType = this._getMimeType(filepath);
|
||||
res.writeHead(200, {
|
||||
'Content-Type': mimeType,
|
||||
'Cache-Control': 'no-cache'
|
||||
});
|
||||
res.end(data);
|
||||
});
|
||||
}
|
||||
|
||||
_send404(res) {
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||
res.end('Not Found');
|
||||
}
|
||||
|
||||
_getMimeType(filepath) {
|
||||
const ext = path.extname(filepath).toLowerCase();
|
||||
return MIME_TYPES[ext] || 'application/octet-stream';
|
||||
}
|
||||
}
|
||||
|
||||
class ExplorerServer {
|
||||
constructor(config) {
|
||||
this.config = config;
|
||||
this.fileServer = new FileServer(__dirname);
|
||||
this.server = null;
|
||||
}
|
||||
|
||||
start() {
|
||||
this.server = http.createServer((req, res) => {
|
||||
this.fileServer.serve(req, res);
|
||||
});
|
||||
|
||||
this.server.listen(this.config.port, this.config.host, () => {
|
||||
console.log(`Ficons Explorer running at http://${this.config.host}:${this.config.port}/`);
|
||||
console.log('Press Ctrl+C to stop the server');
|
||||
});
|
||||
|
||||
this.server.on('error', (err) => this._handleError(err));
|
||||
}
|
||||
|
||||
_handleError(err) {
|
||||
if (err.code === 'EADDRINUSE') {
|
||||
console.error(`Error: Port ${this.config.port} is already in use.`);
|
||||
} else {
|
||||
console.error('Server error:', err.message);
|
||||
}
|
||||
process.exit(EXIT_CODES.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CLI LAYER
|
||||
// ============================================================================
|
||||
|
||||
class CliParser {
|
||||
static parse(args) {
|
||||
if (args.length === 0) {
|
||||
return { error: 'No command provided' };
|
||||
}
|
||||
|
||||
const { values, positionals } = parseArgs({
|
||||
args,
|
||||
options: {
|
||||
help: { type: 'boolean', short: 'h' },
|
||||
variation: { type: 'string', short: 'v' },
|
||||
},
|
||||
allowPositionals: true,
|
||||
});
|
||||
|
||||
return {
|
||||
help: values.help,
|
||||
command: positionals[0]?.toLowerCase(),
|
||||
args: positionals.slice(1),
|
||||
variation: values.variation
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class SearchCommand {
|
||||
constructor(iconLoader) {
|
||||
this.iconLoader = iconLoader;
|
||||
}
|
||||
|
||||
execute(positionals, variation) {
|
||||
const { query, variation: detectedVariation } = this._parseInput(positionals, variation);
|
||||
|
||||
if (!query) {
|
||||
console.error('Error: search command requires a query.');
|
||||
console.log('Usage: npx @invisi/ficons search <query> [options]');
|
||||
process.exit(EXIT_CODES.ERROR);
|
||||
}
|
||||
|
||||
const icons = this.iconLoader.load();
|
||||
const searcher = new IconSearcher(icons);
|
||||
const matches = searcher.search(query, detectedVariation);
|
||||
|
||||
console.log(ResultFormatter.formatJson(matches));
|
||||
}
|
||||
|
||||
_parseInput(positionals, variation) {
|
||||
let effectiveVariation = variation;
|
||||
|
||||
// Auto-detect variation from last positional arg
|
||||
if (!effectiveVariation && positionals.length > 1) {
|
||||
const prefixes = this.iconLoader.getPrefixes();
|
||||
const lastArg = positionals[positionals.length - 1].toLowerCase();
|
||||
|
||||
if (prefixes.has(lastArg)) {
|
||||
effectiveVariation = lastArg;
|
||||
positionals.pop();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
query: positionals.join(' ').trim(),
|
||||
variation: effectiveVariation
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class HelpPrinter {
|
||||
static print() {
|
||||
console.log('Usage: npx @invisi/ficons <command> [options]');
|
||||
console.log('');
|
||||
console.log('Commands:');
|
||||
console.log(' explore Start local HTTP server and open icon explorer');
|
||||
console.log(' search <query> Search for icons by name or tags');
|
||||
console.log('');
|
||||
console.log('Options:');
|
||||
console.log(' -v, --variation <prefix> Filter by style variation (e.g., rs, rr, bs)');
|
||||
console.log(' -h, --help Show this help message');
|
||||
console.log('');
|
||||
console.log('Examples:');
|
||||
console.log(' npx @invisi/ficons explore');
|
||||
console.log(' npx @invisi/ficons search camera');
|
||||
console.log(' npx @invisi/ficons search arrow rr');
|
||||
console.log(' npx @invisi/ficons search home --variation rs');
|
||||
}
|
||||
}
|
||||
|
||||
class CliApplication {
|
||||
constructor() {
|
||||
this.iconLoader = new IconLoader(CONFIG.paths.iconData);
|
||||
this.searchCommand = new SearchCommand(this.iconLoader);
|
||||
}
|
||||
|
||||
run(args) {
|
||||
const parsed = CliParser.parse(args);
|
||||
|
||||
if (parsed.error || parsed.help) {
|
||||
HelpPrinter.print();
|
||||
process.exit(parsed.error ? EXIT_CODES.ERROR : EXIT_CODES.SUCCESS);
|
||||
}
|
||||
|
||||
switch (parsed.command) {
|
||||
case COMMANDS.EXPLORE:
|
||||
new ExplorerServer(CONFIG).start();
|
||||
break;
|
||||
|
||||
case COMMANDS.SEARCH:
|
||||
this.searchCommand.execute(parsed.args, parsed.variation);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error(`Unknown command: ${parsed.command}`);
|
||||
console.log('Run "npx @invisi/ficons --help" for usage information.');
|
||||
process.exit(EXIT_CODES.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ENTRY POINT
|
||||
// ============================================================================
|
||||
|
||||
const app = new CliApplication();
|
||||
app.run(process.argv.slice(2));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user