@stnd/icon 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/embedded.js +22 -0
- package/icons/ph/obsidian-color.svg +6 -0
- package/icons/ph/obsidian.svg +3 -0
- package/icons/simple-icons/obsidian.svg +3 -0
- package/package.json +1 -1
- package/resolve.server.js +49 -37
- package/tokens.js +2 -0
package/CHANGELOG.md
CHANGED
package/embedded.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedded / Custom SVG Icons
|
|
3
|
+
*
|
|
4
|
+
* These icons are bundled directly into JS memory so they are guaranteed to
|
|
5
|
+
* resolve instantly with 0 disk I/O and 0 external network requests, even in
|
|
6
|
+
* serverless runtimes (like Cloudflare Workers SSR) where Node.js filesystem
|
|
7
|
+
* access (`fs.readFile`) is unavailable and the icon is not in upstream Iconify.
|
|
8
|
+
*/
|
|
9
|
+
export const embeddedIcons = {
|
|
10
|
+
"ph:obsidian": `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
|
11
|
+
<path d="M19.355 18.538a68.967 68.959 0 0 0 1.858-2.954.81.81 0 0 0-.062-.9c-.516-.685-1.504-2.075-2.042-3.362-.553-1.321-.636-3.375-.64-4.377a1.707 1.707 0 0 0-.358-1.05l-3.198-4.064a3.744 3.744 0 0 1-.076.543c-.106.503-.307 1.004-.536 1.5-.134.29-.29.6-.446.914l-.31.626c-.516 1.068-.997 2.227-1.132 3.59-.124 1.26.046 2.73.815 4.481.128.011.257.025.386.044a6.363 6.363 0 0 1 3.326 1.505c.916.79 1.744 1.922 2.415 3.5zM8.199 22.569c.073.012.146.02.22.02.78.024 2.095.092 3.16.29.87.16 2.593.64 4.01 1.055 1.083.316 2.198-.548 2.355-1.664.114-.814.33-1.735.725-2.58l-.01.005c-.67-1.87-1.522-3.078-2.416-3.849a5.295 5.295 0 0 0-2.778-1.257c-1.54-.216-2.952.19-3.84.45.532 2.218.368 4.829-1.425 7.531zM5.533 9.938c-.023.1-.056.197-.098.29L2.82 16.059a1.602 1.602 0 0 0 .313 1.772l4.116 4.24c2.103-3.101 1.796-6.02.836-8.3-.728-1.73-1.832-3.081-2.55-3.831zM9.32 14.01c.615-.183 1.606-.465 2.745-.534-.683-1.725-.848-3.233-.716-4.577.154-1.552.7-2.847 1.235-3.95.113-.235.223-.454.328-.664.149-.297.288-.577.419-.86.217-.47.379-.885.46-1.27.08-.38.08-.72-.014-1.043-.095-.325-.297-.675-.68-1.06a1.6 1.6 0 0 0-1.475.36l-4.95 4.452a1.602 1.602 0 0 0-.513.952l-.427 2.83c.672.59 2.328 2.316 3.335 4.711.09.21.175.43.253.653z"/>
|
|
12
|
+
</svg>`,
|
|
13
|
+
"simple-icons:obsidian": `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
|
14
|
+
<path d="M19.355 18.538a68.967 68.959 0 0 0 1.858-2.954.81.81 0 0 0-.062-.9c-.516-.685-1.504-2.075-2.042-3.362-.553-1.321-.636-3.375-.64-4.377a1.707 1.707 0 0 0-.358-1.05l-3.198-4.064a3.744 3.744 0 0 1-.076.543c-.106.503-.307 1.004-.536 1.5-.134.29-.29.6-.446.914l-.31.626c-.516 1.068-.997 2.227-1.132 3.59-.124 1.26.046 2.73.815 4.481.128.011.257.025.386.044a6.363 6.363 0 0 1 3.326 1.505c.916.79 1.744 1.922 2.415 3.5zM8.199 22.569c.073.012.146.02.22.02.78.024 2.095.092 3.16.29.87.16 2.593.64 4.01 1.055 1.083.316 2.198-.548 2.355-1.664.114-.814.33-1.735.725-2.58l-.01.005c-.67-1.87-1.522-3.078-2.416-3.849a5.295 5.295 0 0 0-2.778-1.257c-1.54-.216-2.952.19-3.84.45.532 2.218.368 4.829-1.425 7.531zM5.533 9.938c-.023.1-.056.197-.098.29L2.82 16.059a1.602 1.602 0 0 0 .313 1.772l4.116 4.24c2.103-3.101 1.796-6.02.836-8.3-.728-1.73-1.832-3.081-2.55-3.831zM9.32 14.01c.615-.183 1.606-.465 2.745-.534-.683-1.725-.848-3.233-.716-4.577.154-1.552.7-2.847 1.235-3.95.113-.235.223-.454.328-.664.149-.297.288-.577.419-.86.217-.47.379-.885.46-1.27.08-.38.08-.72-.014-1.043-.095-.325-.297-.675-.68-1.06a1.6 1.6 0 0 0-1.475.36l-4.95 4.452a1.602 1.602 0 0 0-.513.952l-.427 2.83c.672.59 2.328 2.316 3.335 4.711.09.21.175.43.253.653z"/>
|
|
15
|
+
</svg>`,
|
|
16
|
+
"ph:obsidian-color": `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
|
17
|
+
<path fill="#7C3AED" d="m6.919 14.595c.64-.19 1.673-.484 2.86-.556-.712-1.797-.884-3.37-.746-4.77.16-1.616.73-2.966 1.286-4.114.118-.245.232-.473.342-.692.155-.31.3-.6.436-.895.227-.49.395-.922.48-1.324.084-.395.084-.748-.015-1.087-.099-.339-.31-.704-.71-1.104-.522-.135-1.101-.016-1.535.374L4.18 5.066c-.287.259-.476.61-.534.992l-.444 2.947c.699.618 2.424 2.414 3.473 4.906.094.223.182.45.264.684z"/>
|
|
18
|
+
<path fill="#6D28D9" d="m2.973 10.351c-.024.104-.058.205-.102.303l-2.725 6.073c-.28.624-.15 1.355.325 1.846l4.289 4.416c2.19-3.23 1.87-6.27.87-8.646-.758-1.8-1.908-3.21-2.657-3.992z"/>
|
|
19
|
+
<path fill="#8B5CF6" d="m5.75 23.51c.076.011.152.019.229.021.813.024 2.182.095 3.292.3.905.166 2.7.668 4.178 1.1 1.127.33 2.288-.57 2.452-1.733.119-.848.343-1.808.755-2.687l-.01.003c-.698-1.947-1.586-3.204-2.517-4.007-.929-.8-1.928-1.174-2.894-1.31-1.604-.225-3.075.197-4.001.469.555 2.31.384 5.03-1.484 7.844z"/>
|
|
20
|
+
<path fill="#A78BFA" d="m17.37 19.31c.927-1.398 1.587-2.486 1.936-3.076.174-.295.142-.664-.064-.938-.538-.713-1.567-2.16-2.127-3.502-.577-1.378-.663-3.518-.668-4.561-.002-.396-.127-.783-.372-1.095l-3.331-4.232c-.012.19-.04.38-.08.568-.11.524-.32 1.045-.558 1.56-.14.303-.301.626-.465.953-.108.218-.218.437-.322.652-.538 1.113-1.04 2.32-1.18 3.74-.13 1.314.048 2.844.849 4.67.133.012.267.027.402.046 1.149.161 2.355.611 3.465 1.568.954.823 1.816 2.001 2.515 3.646z"/>
|
|
21
|
+
</svg>`,
|
|
22
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
|
2
|
+
<path fill="#7C3AED" d="m6.919 14.595c.64-.19 1.673-.484 2.86-.556-.712-1.797-.884-3.37-.746-4.77.16-1.616.73-2.966 1.286-4.114.118-.245.232-.473.342-.692.155-.31.3-.6.436-.895.227-.49.395-.922.48-1.324.084-.395.084-.748-.015-1.087-.099-.339-.31-.704-.71-1.104-.522-.135-1.101-.016-1.535.374L4.18 5.066c-.287.259-.476.61-.534.992l-.444 2.947c.699.618 2.424 2.414 3.473 4.906.094.223.182.45.264.684z"/>
|
|
3
|
+
<path fill="#6D28D9" d="m2.973 10.351c-.024.104-.058.205-.102.303l-2.725 6.073c-.28.624-.15 1.355.325 1.846l4.289 4.416c2.19-3.23 1.87-6.27.87-8.646-.758-1.8-1.908-3.21-2.657-3.992z"/>
|
|
4
|
+
<path fill="#8B5CF6" d="m5.75 23.51c.076.011.152.019.229.021.813.024 2.182.095 3.292.3.905.166 2.7.668 4.178 1.1 1.127.33 2.288-.57 2.452-1.733.119-.848.343-1.808.755-2.687l-.01.003c-.698-1.947-1.586-3.204-2.517-4.007-.929-.8-1.928-1.174-2.894-1.31-1.604-.225-3.075.197-4.001.469.555 2.31.384 5.03-1.484 7.844z"/>
|
|
5
|
+
<path fill="#A78BFA" d="m17.37 19.31c.927-1.398 1.587-2.486 1.936-3.076.174-.295.142-.664-.064-.938-.538-.713-1.567-2.16-2.127-3.502-.577-1.378-.663-3.518-.668-4.561-.002-.396-.127-.783-.372-1.095l-3.331-4.232c-.012.19-.04.38-.08.568-.11.524-.32 1.045-.558 1.56-.14.303-.301.626-.465.953-.108.218-.218.437-.322.652-.538 1.113-1.04 2.32-1.18 3.74-.13 1.314.048 2.844.849 4.67.133.012.267.027.402.046 1.149.161 2.355.611 3.465 1.568.954.823 1.816 2.001 2.515 3.646z"/>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
|
2
|
+
<path d="M19.355 18.538a68.967 68.959 0 0 0 1.858-2.954.81.81 0 0 0-.062-.9c-.516-.685-1.504-2.075-2.042-3.362-.553-1.321-.636-3.375-.64-4.377a1.707 1.707 0 0 0-.358-1.05l-3.198-4.064a3.744 3.744 0 0 1-.076.543c-.106.503-.307 1.004-.536 1.5-.134.29-.29.6-.446.914l-.31.626c-.516 1.068-.997 2.227-1.132 3.59-.124 1.26.046 2.73.815 4.481.128.011.257.025.386.044a6.363 6.363 0 0 1 3.326 1.505c.916.79 1.744 1.922 2.415 3.5zM8.199 22.569c.073.012.146.02.22.02.78.024 2.095.092 3.16.29.87.16 2.593.64 4.01 1.055 1.083.316 2.198-.548 2.355-1.664.114-.814.33-1.735.725-2.58l-.01.005c-.67-1.87-1.522-3.078-2.416-3.849a5.295 5.295 0 0 0-2.778-1.257c-1.54-.216-2.952.19-3.84.45.532 2.218.368 4.829-1.425 7.531zM5.533 9.938c-.023.1-.056.197-.098.29L2.82 16.059a1.602 1.602 0 0 0 .313 1.772l4.116 4.24c2.103-3.101 1.796-6.02.836-8.3-.728-1.73-1.832-3.081-2.55-3.831zM9.32 14.01c.615-.183 1.606-.465 2.745-.534-.683-1.725-.848-3.233-.716-4.577.154-1.552.7-2.847 1.235-3.95.113-.235.223-.454.328-.664.149-.297.288-.577.419-.86.217-.47.379-.885.46-1.27.08-.38.08-.72-.014-1.043-.095-.325-.297-.675-.68-1.06a1.6 1.6 0 0 0-1.475.36l-4.95 4.452a1.602 1.602 0 0 0-.513.952l-.427 2.83c.672.59 2.328 2.316 3.335 4.711.09.21.175.43.253.653z"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
|
2
|
+
<path d="M19.355 18.538a68.967 68.959 0 0 0 1.858-2.954.81.81 0 0 0-.062-.9c-.516-.685-1.504-2.075-2.042-3.362-.553-1.321-.636-3.375-.64-4.377a1.707 1.707 0 0 0-.358-1.05l-3.198-4.064a3.744 3.744 0 0 1-.076.543c-.106.503-.307 1.004-.536 1.5-.134.29-.29.6-.446.914l-.31.626c-.516 1.068-.997 2.227-1.132 3.59-.124 1.26.046 2.73.815 4.481.128.011.257.025.386.044a6.363 6.363 0 0 1 3.326 1.505c.916.79 1.744 1.922 2.415 3.5zM8.199 22.569c.073.012.146.02.22.02.78.024 2.095.092 3.16.29.87.16 2.593.64 4.01 1.055 1.083.316 2.198-.548 2.355-1.664.114-.814.33-1.735.725-2.58l-.01.005c-.67-1.87-1.522-3.078-2.416-3.849a5.295 5.295 0 0 0-2.778-1.257c-1.54-.216-2.952.19-3.84.45.532 2.218.368 4.829-1.425 7.531zM5.533 9.938c-.023.1-.056.197-.098.29L2.82 16.059a1.602 1.602 0 0 0 .313 1.772l4.116 4.24c2.103-3.101 1.796-6.02.836-8.3-.728-1.73-1.832-3.081-2.55-3.831zM9.32 14.01c.615-.183 1.606-.465 2.745-.534-.683-1.725-.848-3.233-.716-4.577.154-1.552.7-2.847 1.235-3.95.113-.235.223-.454.328-.664.149-.297.288-.577.419-.86.217-.47.379-.885.46-1.27.08-.38.08-.72-.014-1.043-.095-.325-.297-.675-.68-1.06a1.6 1.6 0 0 0-1.475.36l-4.95 4.452a1.602 1.602 0 0 0-.513.952l-.427 2.83c.672.59 2.328 2.316 3.335 4.711.09.21.175.43.253.653z"/>
|
|
3
|
+
</svg>
|
package/package.json
CHANGED
package/resolve.server.js
CHANGED
|
@@ -3,6 +3,8 @@ import * as path from "path";
|
|
|
3
3
|
import * as fs from "fs/promises";
|
|
4
4
|
import { getIconData, iconToSVG, iconToHTML, replaceIDs } from "@iconify/utils";
|
|
5
5
|
|
|
6
|
+
import { embeddedIcons } from "./embedded.js";
|
|
7
|
+
|
|
6
8
|
let __filename = "";
|
|
7
9
|
let __dirname = "";
|
|
8
10
|
try {
|
|
@@ -28,8 +30,9 @@ export const usedIcons = new Set();
|
|
|
28
30
|
*
|
|
29
31
|
* Resolution order:
|
|
30
32
|
* 1. In-memory cache (instant)
|
|
31
|
-
* 2.
|
|
32
|
-
* 3.
|
|
33
|
+
* 2. Embedded custom SVGs (offline in-memory, Cloudflare Workers SSR safe)
|
|
34
|
+
* 3. Local SVG file in packages/icon/icons/{prefix}/{name}.svg (offline, curated library)
|
|
35
|
+
* 4. Iconify API fetch (any icon set, last resort)
|
|
33
36
|
*
|
|
34
37
|
* @param {string} iconName Fully qualified icon name, e.g. "ph:house"
|
|
35
38
|
* @param {{ width?: string, height?: string }} [attrs]
|
|
@@ -53,6 +56,14 @@ export async function resolveIcon(iconName, attrs = {}) {
|
|
|
53
56
|
// Track every icon resolved — used by integration.js to copy only needed SVGs
|
|
54
57
|
usedIcons.add(iconName);
|
|
55
58
|
|
|
59
|
+
// 1. In-memory embedded icons (works in Cloudflare Workers SSR where fs.readFile throws)
|
|
60
|
+
if (embeddedIcons[iconName]) {
|
|
61
|
+
const svg = applyDimensions(embeddedIcons[iconName], width, height);
|
|
62
|
+
cache.set(cacheKey, svg);
|
|
63
|
+
return svg;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 2. Local SVG file on disk (Node.js dev & SSG build)
|
|
56
67
|
let svg = await fromLocalFile(prefix, name, width, height);
|
|
57
68
|
if (!svg) svg = await fromApi(prefix, name, width, height);
|
|
58
69
|
|
|
@@ -60,46 +71,47 @@ export async function resolveIcon(iconName, attrs = {}) {
|
|
|
60
71
|
return svg;
|
|
61
72
|
}
|
|
62
73
|
|
|
74
|
+
// ── SVG Dimension & ViewBox Normalizer ───────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
function applyDimensions(rawSvg, width, height) {
|
|
77
|
+
let svg = rawSvg;
|
|
78
|
+
|
|
79
|
+
// Ensure SVG scales properly by injecting a viewBox if missing (case-insensitive)
|
|
80
|
+
if (!/viewBox=/i.test(svg)) {
|
|
81
|
+
const widthMatch = svg.match(
|
|
82
|
+
/<svg[^>]*\swidth\s*=\s*["']?([^"'\s>]+)["']?/i,
|
|
83
|
+
);
|
|
84
|
+
const heightMatch = svg.match(
|
|
85
|
+
/<svg[^>]*\sheight\s*=\s*["']?([^"'\s>]+)["']?/i,
|
|
86
|
+
);
|
|
87
|
+
if (widthMatch && heightMatch) {
|
|
88
|
+
const w = parseFloat(widthMatch[1]);
|
|
89
|
+
const h = parseFloat(heightMatch[1]);
|
|
90
|
+
if (!isNaN(w) && !isNaN(h)) {
|
|
91
|
+
svg = svg.replace(/<svg\b/i, (m) => `${m} viewBox="0 0 ${w} ${h}"`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Apply requested dimensions to the <svg> element
|
|
97
|
+
svg = svg.replace(/<svg([^>]*)>/i, (_, attrs) => {
|
|
98
|
+
let updated = attrs
|
|
99
|
+
.replace(/\s*width\s*=\s*["']?[^"'\s>]+["']?/i, "")
|
|
100
|
+
.replace(/\s*height\s*=\s*["']?[^"'\s>]+["']?/i, "");
|
|
101
|
+
|
|
102
|
+
updated = updated.replace(/\s+/g, " ").trim();
|
|
103
|
+
return `<svg${updated ? " " + updated : ""} width="${width}" height="${height}">`;
|
|
104
|
+
});
|
|
105
|
+
return svg;
|
|
106
|
+
}
|
|
107
|
+
|
|
63
108
|
// ── Local SVG file resolver ──────────────────────────────────────────────────
|
|
64
109
|
|
|
65
110
|
async function fromLocalFile(prefix, name, width, height) {
|
|
66
111
|
const filePath = path.join(LOCAL_ICONS_DIR, prefix, `${name}.svg`);
|
|
67
112
|
try {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// Ensure SVG scales properly by injecting a viewBox if missing (case-insensitive)
|
|
71
|
-
// This handles files that may include XML prologs, comments, or different quote styles.
|
|
72
|
-
if (!/viewBox=/i.test(svg)) {
|
|
73
|
-
const widthMatch = svg.match(
|
|
74
|
-
/<svg[^>]*\swidth\s*=\s*["']?([^"'\s>]+)["']?/i,
|
|
75
|
-
);
|
|
76
|
-
const heightMatch = svg.match(
|
|
77
|
-
/<svg[^>]*\sheight\s*=\s*["']?([^"'\s>]+)["']?/i,
|
|
78
|
-
);
|
|
79
|
-
if (widthMatch && heightMatch) {
|
|
80
|
-
const w = parseFloat(widthMatch[1]);
|
|
81
|
-
const h = parseFloat(heightMatch[1]);
|
|
82
|
-
if (!isNaN(w) && !isNaN(h)) {
|
|
83
|
-
// Insert viewBox after the <svg tag name. Use a case-insensitive match so it works for varied files.
|
|
84
|
-
svg = svg.replace(/<svg\b/i, (m) => `${m} viewBox="0 0 ${w} ${h}"`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Apply requested dimensions to the <svg> element
|
|
90
|
-
// Use a non-anchored, case-insensitive match so we find the opening <svg> even if there
|
|
91
|
-
// is an XML prolog or whitespace before it.
|
|
92
|
-
svg = svg.replace(/<svg([^>]*)>/i, (_, attrs) => {
|
|
93
|
-
// Replace or add width/height attributes (handle single or double quotes and optional spaces)
|
|
94
|
-
let updated = attrs
|
|
95
|
-
.replace(/\s*width\s*=\s*["']?[^"'\s>]+["']?/i, "")
|
|
96
|
-
.replace(/\s*height\s*=\s*["']?[^"'\s>]+["']?/i, "");
|
|
97
|
-
|
|
98
|
-
// Normalize spacing inside the tag to avoid accidental double spaces
|
|
99
|
-
updated = updated.replace(/\s+/g, " ").trim();
|
|
100
|
-
return `<svg${updated ? " " + updated : ""} width="${width}" height="${height}">`;
|
|
101
|
-
});
|
|
102
|
-
return svg;
|
|
113
|
+
const raw = await fs.readFile(filePath, "utf8");
|
|
114
|
+
return applyDimensions(raw, width, height);
|
|
103
115
|
} catch {
|
|
104
116
|
return null;
|
|
105
117
|
}
|
package/tokens.js
CHANGED
|
@@ -27,6 +27,8 @@ export const tokens = {
|
|
|
27
27
|
users: "ph:users",
|
|
28
28
|
|
|
29
29
|
// ── Garden & Launcher ───────────────────────────────────────
|
|
30
|
+
obsidian: "ph:obsidian",
|
|
31
|
+
"obsidian-color": "ph:obsidian-color",
|
|
30
32
|
mycelium: "ph:graph",
|
|
31
33
|
daily: "ph:calendar",
|
|
32
34
|
random: "ph:shuffle",
|