Skip to content

feat: add requested icons + fix Raycast extension#93

Merged
thegdsks merged 2 commits intomainfrom
feat/icon-requests-90-91
Apr 10, 2026
Merged

feat: add requested icons + fix Raycast extension#93
thegdsks merged 2 commits intomainfrom
feat/icon-requests-90-91

Conversation

@thegdsks
Copy link
Copy Markdown
Member

Summary

Test plan

  • pnpm build succeeds within Cloudflare 20K file limit
  • /icons/jinritoutiao/default.svg renders correctly
  • /icons/format-json-online/default.svg renders correctly
  • /api/registry.json includes both new icons
  • /api/categories.json returns updated category counts

thegdsks and others added 2 commits April 10, 2026 15:36
Closes #90, closes #91

Co-Authored-By: Glinr <bot@glincker.com>
Remove individual registry/<slug>.json files to stay within Cloudflare
Pages 20K file limit. Extension now fetches SVGs directly and caches
the registry. Also adds JSX/HTML/DataURI copy formats and updates
metadata to 5,600+ icons.

Co-Authored-By: Glinr <bot@glincker.com>
Copilot AI review requested due to automatic review settings April 10, 2026 20:37
@thegdsks thegdsks merged commit a030761 into main Apr 10, 2026
4 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds two requested brand icons and updates the Raycast extension + static API generation to avoid Cloudflare Pages’ 20K file limit by removing per-icon JSON outputs and switching the extension to use a cached registry plus direct SVG fetching.

Changes:

  • Add new icons: jinritoutiao and format-json-online (SVGs + icons.json entries).
  • Update generate-api.ts to output only registry.json + categories.json (no per-icon detail files).
  • Refactor Raycast extension to use /api/registry.json, client-side filtering, direct SVG fetch per variant, and add “copy as JSX/HTML/Data URI/Hex” actions.

Reviewed changes

Copilot reviewed 7 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/scripts/generate-api.ts Stops generating per-icon registry files; expands registry.json fields for extension use
src/data/icons.json Adds two icon entries and normalizes various Unicode-escaped strings
public/icons/jinritoutiao/default.svg Adds Jinritoutiao SVG asset
public/icons/format-json-online/default.svg Adds Format JSON Online SVG asset
extensions/raycast/src/api.ts Switches to cached registry fetching + direct SVG fetch; adds copy-format helpers
extensions/raycast/src/search-icons.tsx Updates search UX, adds “Copy As …” actions, and uses aliases as keywords
extensions/raycast/README.md Updates counts and documents new copy formats
extensions/raycast/package.json Updates extension metadata strings to 5,600+ icon count
extensions/raycast/CHANGELOG.md Documents fix + new features for v1.1.0

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +11 to +13
* Note: Individual per-icon detail files are NOT generated to stay within
* Cloudflare Pages' 20,000 file deployment limit. Extensions should fetch
* SVG content directly from /icons/{slug}/{variant}.svg.
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

This script no longer generates per-icon detail files; clients still calling the old per-icon endpoints (e.g. extensions/figma/src/main.ts uses /api/registry/{slug} and /api/registry?...) will now 404 unless the site keeps a compatibility route/rewrite. Consider updating those consumers (and API docs) to use /api/registry.json + direct SVG fetching, or add a compatibility endpoint.

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +85
return {
total: data.total,
icons: filtered.slice(0, limit),
};
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

searchIcons() returns total: data.total (the full registry size) even after applying query/category filters, so SearchResult.total becomes inaccurate for pagination/UI. Consider returning the filtered match count (and optionally include the unfiltered total in a separate field if needed).

Copilot uses AI. Check for mistakes.
Comment on lines +144 to +148
function toPascalCase(str: string): string {
return str
.replace(/[^a-zA-Z0-9]+(.)/g, (_, c: string) => c.toUpperCase())
.replace(/^(.)/, (_, c: string) => c.toUpperCase());
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

toPascalCase() can generate an invalid component name when the slug starts with a digit (e.g. "01dotai" => "01dotaiIcon"), making the copied JSX snippet syntactically invalid. Consider ensuring the generated name always starts with a valid identifier character (prefix with "Icon"/"Svg" or "_" when needed).

Copilot uses AI. Check for mistakes.
Comment on lines +150 to +154
function svgToJsxAttrs(svg: string): string {
return svg
.replace(/\bclass="/g, 'className="')
.replace(/\bxmlns="[^"]*"/g, "")
.replace(
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

The SVG->JSX conversion doesn’t handle style="..." attributes, which React expects as a style object. Since some icons include style attributes (e.g. format-json-online has ), the copied JSX can be invalid/ignored. Consider converting style strings to JSX style objects or using a robust transformer (e.g. SVGR).

Copilot uses AI. Check for mistakes.
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 10, 2026

Greptile Summary

This PR adds two new icons (Jinritoutiao and Format JSON Online), fixes the Raycast extension's 404 error by dropping per-icon JSON files (Cloudflare Pages 20K-file limit) in favour of a shared registry.json with direct SVG fetching, and adds JSX/HTML/Data-URI/hex copy actions to the detail view.

The architecture change is sound — the generate-api.ts script now only writes two files, cachedRegistry avoids repeated round-trips, and all new copy helpers are covered by unit-testable pure functions in api.ts.

Confidence Score: 5/5

Safe to merge — all findings are P2 style/quality suggestions with no impact on current runtime behaviour.

The 404 fix is correctly implemented, both new SVG icons are valid, registry caching works as intended, and the new copy-format helpers are pure functions with straightforward logic. The only concerns are a latent svgToJsxAttrs CSS-in-style-block risk (not triggered by any current icon) and a misleading total field that isn't consumed by the UI today.

extensions/raycast/src/api.ts — specifically svgToJsxAttrs and the total field in searchIcons

Important Files Changed

Filename Overview
extensions/raycast/src/api.ts Core API rewrite: adds registry caching, direct SVG fetching, and copy-format helpers (toJsx, toHtmlImg, toDataUri); svgToJsxAttrs has a latent CSS-in-style-block corruption risk
extensions/raycast/src/search-icons.tsx Adds CopyFormatsSection component with JSX/HTML/DataURI/hex actions; new copy shortcuts only surface in detail view, which is intentional given SVG content must be fetched first
src/scripts/generate-api.ts Drops per-icon JSON generation and now only writes registry.json and categories.json, keeping the build within Cloudflare Pages' 20K-file limit
public/icons/format-json-online/default.svg Valid SVG; uses CSS class selectors (.cls-1, .cls-3) in an inline <style> block — global class names could clash when multiple such icons are rendered together
public/icons/jinritoutiao/default.svg Valid SVG using inline fill/fill-rule attributes; no style block or class selectors, no issues
extensions/raycast/package.json Metadata updated to reflect 5,600+ icons; dependency versions unchanged and current
src/data/icons.json Two new entries added (format-json-online, jinritoutiao) with correct slug, hex, categories, variants, and metadata

Sequence Diagram

sequenceDiagram
    participant User
    participant Raycast as Raycast Extension
    participant Cache as Module Cache
    participant API as thesvg.org/api
    participant SVG as thesvg.org/icons

    User->>Raycast: Search / open detail
    Raycast->>Cache: cachedRegistry?
    alt Cache miss
        Cache-->>Raycast: null
        Raycast->>API: GET /api/registry.json
        API-->>Raycast: {total, icons[]}
        Raycast->>Cache: store registry
    else Cache hit
        Cache-->>Raycast: registry
    end
    Raycast-->>User: show filtered icon list

    User->>Raycast: View Details (Cmd+D)
    Raycast->>Cache: get entry from registry
    loop For each variant
        Raycast->>SVG: GET /icons/{slug}/{variant}.svg
        SVG-->>Raycast: SVG content
    end
    Raycast-->>User: IconDetailView with metadata

    User->>Raycast: Copy as JSX / HTML / DataURI / Hex
    Raycast->>Raycast: toJsx() / toHtmlImg() / toDataUri()
    Raycast-->>User: content copied to clipboard
Loading

Reviews (1): Last reviewed commit: "fix: resolve Raycast 404 error, remove p..." | Re-trigger Greptile

Comment on lines +150 to +158
function svgToJsxAttrs(svg: string): string {
return svg
.replace(/\bclass="/g, 'className="')
.replace(/\bxmlns="[^"]*"/g, "")
.replace(
/\b([a-z]+)-([a-z])/g,
(_, a: string, b: string) => `${a}${b.toUpperCase()}`,
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 svgToJsxAttrs corrupts CSS inside <style> blocks

The kebab-to-camelCase regex runs over the entire SVG string, including any <style> block content. CSS property names inside <style> must stay kebab-case; converting them to camelCase breaks the stylesheet. The current SVGs are safe (only fill: properties), but any future icon whose <style> block contains background-color, stroke-width, font-size, etc. will produce invalid CSS in the JSX output.

Consider scoping the attribute rename to only SVG/HTML tag attribute positions, or at minimum restrict it to run only outside <style>...</style> blocks:

function svgToJsxAttrs(svg: string): string {
  const stylePlaceholders: string[] = [];
  const withoutStyles = svg.replace(/<style[\s\S]*?<\/style>/gi, (match) => {
    stylePlaceholders.push(match);
    return `__STYLE_PLACEHOLDER_${stylePlaceholders.length - 1}__`;
  });

  const converted = withoutStyles
    .replace(/\bclass="/g, 'className="')
    .replace(/\bxmlns="[^"]*"/g, "")
    .replace(/\b([a-z]+)-([a-z])/g, (_, a, b) => `${a}${b.toUpperCase()}`);

  return converted.replace(
    /__STYLE_PLACEHOLDER_(\d+)__/g,
    (_, i) => stylePlaceholders[Number(i)],
  );
}

Comment on lines +82 to +85
return {
total: data.total,
icons: filtered.slice(0, limit),
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 total in SearchResult always reflects the unfiltered registry count

searchIcons returns total: data.total (the full 5,600+ registry size) even when the result set is heavily filtered. Any future caller relying on total to show "X results found" will display a misleading number. The field would be more useful as the count of matching icons before the limit slice:

return {
  total: filtered.length,
  icons: filtered.slice(0, limit),
};

Comment on lines +160 to +163
export function toJsx(svg: string, componentName: string): string {
const name = toPascalCase(componentName);
const jsxSvg = svgToJsxAttrs(svg);
return `export function ${name}Icon(props) {\n return (\n ${jsxSvg.replace(/<svg/, "<svg {...props}")}\n );\n}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Generated JSX is unindented for multi-line SVGs

The template literal inserts the SVG string inline without re-indenting it, so every line after the opening <svg> tag will start at column 0 in the copied output:

export function FormatJsonOnlineIcon(props) {
  return (
    <svg ...>
<defs>...          ← no indent

Passing the SVG through a light indent step would produce cleaner output for users pasting into their codebase.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Icon Request] Jinritoutiao (via thesvg.org) [Icon Request] Format json online (via thesvg.org)

2 participants