@nice2dev/icons 1.3.9 → 1.3.11

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/cjs/NiceIconPicker.js +42 -21
  3. package/dist/cjs/NiceIconValue.js +44 -0
  4. package/dist/cjs/client.js +6 -1
  5. package/dist/cjs/iconHtml.js +0 -18
  6. package/dist/cjs/iconStack.js +3 -1
  7. package/dist/cjs/iconValue.js +473 -0
  8. package/dist/cjs/icons/actions.js +39 -8
  9. package/dist/cjs/icons/files.js +23 -2
  10. package/dist/cjs/icons/fintech.js +12 -5
  11. package/dist/cjs/icons/index.js +1 -0
  12. package/dist/cjs/icons/media.js +7 -4
  13. package/dist/cjs/icons/realestate.js +8 -7
  14. package/dist/cjs/icons/shapes.js +55 -4
  15. package/dist/cjs/index.js +7 -1
  16. package/dist/cjs/nicetodev-icons.css +1 -1
  17. package/dist/esm/NiceIconPicker.js +43 -22
  18. package/dist/esm/NiceIconValue.js +41 -0
  19. package/dist/esm/client.js +3 -1
  20. package/dist/esm/iconHtml.js +1 -18
  21. package/dist/esm/iconStack.js +3 -1
  22. package/dist/esm/iconValue.js +470 -0
  23. package/dist/esm/icons/actions.js +39 -9
  24. package/dist/esm/icons/files.js +23 -2
  25. package/dist/esm/icons/fintech.js +12 -5
  26. package/dist/esm/icons/index.js +1 -1
  27. package/dist/esm/icons/media.js +8 -5
  28. package/dist/esm/icons/realestate.js +8 -7
  29. package/dist/esm/icons/shapes.js +55 -4
  30. package/dist/esm/index.js +4 -2
  31. package/dist/esm/nicetodev-icons.css +1 -1
  32. package/dist/types/NiceIconPicker.d.ts +22 -16
  33. package/dist/types/NiceIconValue.d.ts +40 -0
  34. package/dist/types/client.d.ts +6 -2
  35. package/dist/types/iconHtml.d.ts +1 -23
  36. package/dist/types/iconValue.d.ts +22 -0
  37. package/dist/types/icons/actions.d.ts +1 -0
  38. package/dist/types/icons/index.d.ts +1 -1
  39. package/dist/types/index.d.ts +6 -2
  40. package/package.json +2 -2
@@ -1,6 +1,3 @@
1
- import { jsx, Fragment } from 'react/jsx-runtime';
2
- import { renderIconStack } from './iconStack.js';
3
-
4
1
  /**
5
2
  * An icon — or a stack of icons — kept as HTML (1.3.9): the `<svg>` NiceIconPicker draws, ready to
6
3
  * store in a database and to show anywhere, even without React. What it was drawn from rides along
@@ -99,19 +96,5 @@ function scopeIconHtmlIds(markup) {
99
96
  const name = hash(rename((id) => `#${index.get(id)}`));
100
97
  return rename((id) => `ntd-${name}-${index.get(id)}`);
101
98
  }
102
- /**
103
- * NiceIconHtml (1.3.9) — shows an icon kept as HTML in a React page by drawing it again from its
104
- * data attributes; the markup itself is never injected, so HTML from a database cannot run anything.
105
- * A plain export name (`'NtdStar'`) works too. The rest of the props go to the base icon.
106
- */
107
- function NiceIconHtml({ html, icons, values, fallback = null, ...baseProps }) {
108
- var _a;
109
- const source = (_a = parseIconHtml(html)) !== null && _a !== void 0 ? _a : (html && !html.includes('<') ? { layers: [{ id: '1', icon: html }] } : null);
110
- const drawn = source ? renderIconStack(source.layers, icons, { baseProps, values }) : null;
111
- return jsx(Fragment, { children: drawn !== null && drawn !== void 0 ? drawn : fallback });
112
- }
113
- if (process.env.NODE_ENV !== 'production') {
114
- NiceIconHtml.displayName = 'NiceIconHtml';
115
- }
116
99
 
117
- export { NiceIconHtml, iconHtmlAttributes, parseIconHtml, scopeIconHtmlIds };
100
+ export { iconHtmlAttributes, parseIconHtml, scopeIconHtmlIds };
@@ -125,7 +125,9 @@ function niceIconStackCode(layers) {
125
125
  * One SVG, as nesting the icons by hand would give.
126
126
  */
127
127
  function renderIconStack(layers, icons, { baseProps, values } = {}) {
128
- const resolve = (name) => (typeof icons === 'function' ? icons(name) : icons[name]);
128
+ const lookup = (name) => (typeof icons === 'function' ? icons(name) : icons[name]);
129
+ // a stack kept as text names its icons without `Ntd` (1.3.10)
130
+ const resolve = (name) => { var _a; return (_a = lookup(name)) !== null && _a !== void 0 ? _a : lookup(`Ntd${name}`); };
129
131
  const draw = (layer, parent) => {
130
132
  var _a;
131
133
  const Icon = resolve(layer.icon);
@@ -0,0 +1,470 @@
1
+ import { parseIconHtml } from './iconHtml.js';
2
+ import { iconDisplayName, stackChildrenOf } from './iconStack.js';
3
+
4
+ /**
5
+ * An icon — or a stack of icons — kept as short text (1.3.10): what NiceIconPicker stores with
6
+ * `valueFormat="name"` (the default) or `"json"`, as short as a database value can be and still be
7
+ * read by people.
8
+ *
9
+ * **The code** (`'name'`) — a plain icon is its name (`NtdFolder`); a stack nests like a function
10
+ * call, the parent first, each setting behind a sign of its own:
11
+ *
12
+ * ```
13
+ * Folder(Star@tr) a star in the folder's top-right corner
14
+ * Bell(Circle@tr'3') a circle with "3" in the bell's corner
15
+ * Folder(Star@tr%40^15,Lock@bl) two icons in the folder: 40 %, turned 15°; a lock
16
+ * User:o(Circle@br#2a2{count}'5'#fff) an outline user, a green circle bound to `count`
17
+ * ```
18
+ *
19
+ * | sign | meaning | example |
20
+ * |---|---|---|
21
+ * | `( , )` | what sits in the icon, side by side | `Folder(Star,Lock)` |
22
+ * | `:` | variant — `so` solid, `sh` sharp, `o` outline, `w` wireframe, `d` duotone, `c` cutout, `r` rounded, `m` mini, `3d`, `v` vivid | `:o` |
23
+ * | `@` | position in the parent — `c t b l r tl tr bl br`, `h…` halfway (`ht`, `hbr`…); the parent's own spot when absent | `@tr` |
24
+ * | `%` | size, percent of the parent | `%40` |
25
+ * | `^` | turn, degrees clockwise | `^-30` |
26
+ * | `~` | nudge right / down, percent of the parent | `~5/-3` |
27
+ * | `#` | colours: primary, secondary, accent (`##0f0` — secondary only) | `#2a2` |
28
+ * | `'…'` | text; `#` and `%` after it are the text's colour and size | `'3'#fff%60` |
29
+ * | `{…}` | the value the text is bound to (the text after it is a sample) | `{count}'5'` |
30
+ *
31
+ * Names drop `Ntd` inside a stack. A name or colour holding any of the signs goes in backticks
32
+ * (`` `fa:home` ``); `\` escapes inside quotes.
33
+ *
34
+ * **JSON** (`'json'`) — the same tree as JsonML: `["Folder",["Star",{"a":"tr"}]]`, a node being
35
+ * `[name, {settings}?, ...nodes, "text"?]` with the settings `v a s r x y p sc ac b tc ts` written
36
+ * as in the code; a plain icon is its name as a JSON string.
37
+ */
38
+ const VARIANT_CODE = {
39
+ solid: 'so',
40
+ sharp: 'sh',
41
+ outline: 'o',
42
+ wireframe: 'w',
43
+ duotone: 'd',
44
+ cutout: 'c',
45
+ rounded: 'r',
46
+ mini: 'm',
47
+ '3d': '3d',
48
+ vivid: 'v',
49
+ };
50
+ const ALIGN_CODE = {
51
+ center: 'c',
52
+ top: 't',
53
+ bottom: 'b',
54
+ left: 'l',
55
+ right: 'r',
56
+ 'top-left': 'tl',
57
+ 'top-right': 'tr',
58
+ 'bottom-left': 'bl',
59
+ 'bottom-right': 'br',
60
+ 'half-top': 'ht',
61
+ 'half-bottom': 'hb',
62
+ 'half-left': 'hl',
63
+ 'half-right': 'hr',
64
+ 'half-top-left': 'htl',
65
+ 'half-top-right': 'htr',
66
+ 'half-bottom-left': 'hbl',
67
+ 'half-bottom-right': 'hbr',
68
+ };
69
+ /** A code or a full name → the value; an unknown one (a newer writer's) reads as unset. */
70
+ function reverse(codes) {
71
+ const map = new Map();
72
+ for (const [full, code] of Object.entries(codes)) {
73
+ map.set(code, full);
74
+ map.set(full, full);
75
+ }
76
+ return map;
77
+ }
78
+ const VARIANTS = reverse(VARIANT_CODE);
79
+ const ALIGNS = reverse({ ...ALIGN_CODE, perfect: 'p' });
80
+ /** The signs of the code; a name or colour holding one of them goes in backticks. */
81
+ const SIGNS = new Set(['(', ')', ',', ':', '@', '%', '^', '~', '#', "'", '{', '}', '`', '\\']);
82
+ /** A plain icon: one layer and nothing set on it but its name. */
83
+ function isPlain(layers) {
84
+ return (layers.length === 1 &&
85
+ Object.entries(layers[0]).every(([key, value]) => key === 'id' || key === 'icon' || value === undefined));
86
+ }
87
+ function quote(text, mark) {
88
+ return mark + text.replace(/[\\`']/g, (c) => (c === mark || c === '\\' ? `\\${c}` : c)) + mark;
89
+ }
90
+ function writeName(name) {
91
+ const needsQuotes = name === '' ||
92
+ name !== name.trim() ||
93
+ [...name].some((c) => SIGNS.has(c)) ||
94
+ /^["[<]/.test(name);
95
+ return needsQuotes ? quote(name, '`') : name;
96
+ }
97
+ function writeNumber(n) {
98
+ return String(Math.round(n * 1000) / 1000);
99
+ }
100
+ /** `#22aa22` → `2a2`, any other hex as is; another colour (`var(--x)`) in backticks. */
101
+ function writeColor(color) {
102
+ if (!color) {
103
+ return '';
104
+ }
105
+ const hex = /^#([0-9a-f]{3,8})$/i.exec(color);
106
+ if (!hex) {
107
+ return quote(color, '`');
108
+ }
109
+ const h = hex[1].toLowerCase();
110
+ return h.length === 6 && h[0] === h[1] && h[2] === h[3] && h[4] === h[5] ? h[0] + h[2] + h[4] : h;
111
+ }
112
+ function writeCode(layers, shortName) {
113
+ const write = (layer, isBase) => {
114
+ var _a, _b, _c, _d;
115
+ let out = writeName(shortName(layer.icon));
116
+ if (layer.variant) {
117
+ out += `:${(_a = VARIANT_CODE[layer.variant]) !== null && _a !== void 0 ? _a : layer.variant}`;
118
+ }
119
+ if (!isBase) {
120
+ if (layer.align && layer.align !== 'perfect') {
121
+ out += `@${(_b = ALIGN_CODE[layer.align]) !== null && _b !== void 0 ? _b : layer.align}`;
122
+ }
123
+ if (layer.size !== undefined) {
124
+ out += `%${writeNumber(layer.size * 100)}`;
125
+ }
126
+ if (layer.rotation !== undefined) {
127
+ out += `^${writeNumber(layer.rotation)}`;
128
+ }
129
+ if (layer.offsetX || layer.offsetY) {
130
+ out += `~${writeNumber((_c = layer.offsetX) !== null && _c !== void 0 ? _c : 0)}`;
131
+ if (layer.offsetY) {
132
+ out += `/${writeNumber(layer.offsetY)}`;
133
+ }
134
+ }
135
+ }
136
+ const colors = [layer.primaryColor, layer.secondaryColor, layer.accentColor];
137
+ const lastColor = colors.reduce((last, c, i) => (c ? i : last), -1);
138
+ for (let i = 0; i <= lastColor; i++) {
139
+ out += `#${writeColor(colors[i])}`;
140
+ }
141
+ const bound = !!layer.textBinding && /^[A-Za-z_$][\w$]*$/.test(layer.textBinding);
142
+ if (bound) {
143
+ out += `{${layer.textBinding}}`;
144
+ }
145
+ // text settings need a text part to follow; an empty text carries them
146
+ if (layer.text || (!bound && (layer.textColor || layer.textSize !== undefined))) {
147
+ out += quote((_d = layer.text) !== null && _d !== void 0 ? _d : '', "'");
148
+ }
149
+ if (layer.textColor) {
150
+ out += `#${writeColor(layer.textColor)}`;
151
+ }
152
+ if (layer.textSize !== undefined) {
153
+ out += `%${writeNumber(layer.textSize)}`;
154
+ }
155
+ const inner = stackChildrenOf(layers, layer.id);
156
+ return inner.length > 0 ? `${out}(${inner.map((l) => write(l, false)).join(',')})` : out;
157
+ };
158
+ return write(layers[0], true);
159
+ }
160
+ function writeJson(layers, shortName) {
161
+ const write = (layer, isBase) => {
162
+ var _a, _b;
163
+ const s = {};
164
+ const set = (key, value) => {
165
+ if (value !== undefined && value !== '' && value !== 0) {
166
+ s[key] = value;
167
+ }
168
+ };
169
+ set('v', layer.variant && ((_a = VARIANT_CODE[layer.variant]) !== null && _a !== void 0 ? _a : layer.variant));
170
+ if (!isBase) {
171
+ if (layer.align && layer.align !== 'perfect') {
172
+ set('a', (_b = ALIGN_CODE[layer.align]) !== null && _b !== void 0 ? _b : layer.align);
173
+ }
174
+ if (layer.size !== undefined) {
175
+ s.s = Math.round(layer.size * 100000) / 1000;
176
+ }
177
+ if (layer.rotation !== undefined) {
178
+ s.r = layer.rotation;
179
+ }
180
+ set('x', layer.offsetX);
181
+ set('y', layer.offsetY);
182
+ }
183
+ set('p', layer.primaryColor);
184
+ set('sc', layer.secondaryColor);
185
+ set('ac', layer.accentColor);
186
+ set('b', layer.textBinding);
187
+ set('tc', layer.textColor);
188
+ if (layer.textSize !== undefined) {
189
+ s.ts = layer.textSize;
190
+ }
191
+ const node = [shortName(layer.icon)];
192
+ if (Object.keys(s).length > 0) {
193
+ node.push(s);
194
+ }
195
+ for (const child of stackChildrenOf(layers, layer.id)) {
196
+ node.push(write(child, false));
197
+ }
198
+ if (layer.text) {
199
+ node.push(layer.text);
200
+ }
201
+ return node;
202
+ };
203
+ return write(layers[0], true);
204
+ }
205
+ /**
206
+ * Writes an icon or a stack as short text (1.3.10): `'name'` — a plain icon's name, a stack's code
207
+ * (`Folder(Star@tr)`) — or `'json'` (`["Folder",["Star",{"a":"tr"}]]`). Empty for no layers.
208
+ * `parseIconValue` reads it back; the ids of the layers are not kept.
209
+ */
210
+ function formatIconValue(layers, format = 'name', { shortName = iconDisplayName } = {}) {
211
+ if (layers.length === 0) {
212
+ return '';
213
+ }
214
+ if (isPlain(layers)) {
215
+ return format === 'json' ? JSON.stringify(layers[0].icon) : layers[0].icon;
216
+ }
217
+ return format === 'json'
218
+ ? JSON.stringify(writeJson(layers, shortName))
219
+ : writeCode(layers, shortName);
220
+ }
221
+ /* ─── Reading ─── */
222
+ class IconValueError extends Error {
223
+ }
224
+ /** Collects the layers in document order, ids `'1'`, `'2'`…; what sits in the base has no parent. */
225
+ class LayerSink {
226
+ constructor() {
227
+ this.layers = [];
228
+ }
229
+ add(layer, parent, isBase) {
230
+ const data = isBase
231
+ ? // where a layer sits means nothing for the base
232
+ { ...layer, align: undefined, size: undefined, rotation: undefined }
233
+ : layer;
234
+ if (isBase || !data.offsetX) {
235
+ data.offsetX = undefined;
236
+ }
237
+ if (isBase || !data.offsetY) {
238
+ data.offsetY = undefined;
239
+ }
240
+ if (data.align === 'perfect') {
241
+ data.align = undefined;
242
+ }
243
+ const id = String(this.layers.length + 1);
244
+ const clean = Object.fromEntries(Object.entries(data).filter(([, v]) => v !== undefined && v !== ''));
245
+ this.layers.push({ id, ...clean, ...(parent && parent !== '1' ? { parent } : {}) });
246
+ return id;
247
+ }
248
+ }
249
+ function readCode(code) {
250
+ const sink = new LayerSink();
251
+ let i = 0;
252
+ const peek = () => code[i];
253
+ const skipSpaces = () => {
254
+ while (i < code.length && /\s/.test(code[i])) {
255
+ i++;
256
+ }
257
+ };
258
+ const quoted = (mark) => {
259
+ i++;
260
+ let out = '';
261
+ while (i < code.length && code[i] !== mark) {
262
+ if (code[i] === '\\' && i + 1 < code.length) {
263
+ i++;
264
+ }
265
+ out += code[i];
266
+ i++;
267
+ }
268
+ if (code[i] !== mark) {
269
+ throw new IconValueError('unclosed quote');
270
+ }
271
+ i++;
272
+ return out;
273
+ };
274
+ const name = () => {
275
+ skipSpaces();
276
+ if (peek() === '`') {
277
+ return quoted('`');
278
+ }
279
+ const start = i;
280
+ while (i < code.length && !SIGNS.has(code[i])) {
281
+ i++;
282
+ }
283
+ const out = code.slice(start, i).trim();
284
+ if (!out) {
285
+ throw new IconValueError('name expected');
286
+ }
287
+ return out;
288
+ };
289
+ const word = () => {
290
+ var _a, _b;
291
+ const hit = (_b = (_a = /^[A-Za-z0-9-]*/.exec(code.slice(i))) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : '';
292
+ i += hit.length;
293
+ return hit;
294
+ };
295
+ const number = () => {
296
+ var _a;
297
+ const hit = (_a = /^-?(?:\d+(?:\.\d*)?|\.\d+)/.exec(code.slice(i))) === null || _a === void 0 ? void 0 : _a[0];
298
+ if (!hit) {
299
+ throw new IconValueError('number expected');
300
+ }
301
+ i += hit.length;
302
+ return Number(hit);
303
+ };
304
+ const color = () => {
305
+ var _a, _b;
306
+ if (peek() === '`') {
307
+ return quoted('`');
308
+ }
309
+ const hit = (_b = (_a = /^[0-9a-fA-F]{0,8}/.exec(code.slice(i))) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : '';
310
+ i += hit.length;
311
+ return hit ? `#${hit.toLowerCase()}` : undefined;
312
+ };
313
+ const node = (parent, isBase) => {
314
+ const layer = { icon: name() };
315
+ const colors = [];
316
+ let settings = true;
317
+ while (settings) {
318
+ const sign = peek();
319
+ if (sign === ':' ||
320
+ sign === '@' ||
321
+ sign === '%' ||
322
+ sign === '^' ||
323
+ sign === '~' ||
324
+ sign === '#') {
325
+ i++;
326
+ }
327
+ if (sign === ':') {
328
+ layer.variant = VARIANTS.get(word());
329
+ }
330
+ else if (sign === '@') {
331
+ layer.align = ALIGNS.get(word());
332
+ }
333
+ else if (sign === '%') {
334
+ layer.size = Math.round(number() * 1000) / 100000;
335
+ }
336
+ else if (sign === '^') {
337
+ layer.rotation = number();
338
+ }
339
+ else if (sign === '~') {
340
+ layer.offsetX = number();
341
+ if (peek() === '/') {
342
+ i++;
343
+ layer.offsetY = number();
344
+ }
345
+ }
346
+ else if (sign === '#') {
347
+ colors.push(color());
348
+ }
349
+ else {
350
+ settings = false;
351
+ }
352
+ }
353
+ [layer.primaryColor, layer.secondaryColor, layer.accentColor] = colors;
354
+ if (peek() === '{') {
355
+ const end = code.indexOf('}', i);
356
+ if (end < 0) {
357
+ throw new IconValueError('unclosed binding');
358
+ }
359
+ layer.textBinding = code.slice(i + 1, end).trim();
360
+ i = end + 1;
361
+ }
362
+ if (peek() === "'") {
363
+ layer.text = quoted("'");
364
+ }
365
+ if (layer.text !== undefined || layer.textBinding) {
366
+ for (let sign = peek(); sign === '#' || sign === '%'; sign = peek()) {
367
+ i++;
368
+ if (sign === '#') {
369
+ layer.textColor = color();
370
+ }
371
+ else {
372
+ layer.textSize = number();
373
+ }
374
+ }
375
+ }
376
+ const id = sink.add(layer, parent, isBase);
377
+ skipSpaces();
378
+ if (peek() === '(') {
379
+ i++;
380
+ let more = true;
381
+ while (more) {
382
+ node(id, false);
383
+ skipSpaces();
384
+ more = peek() === ',';
385
+ if (more) {
386
+ i++;
387
+ }
388
+ }
389
+ if (peek() !== ')') {
390
+ throw new IconValueError(') expected');
391
+ }
392
+ i++;
393
+ skipSpaces();
394
+ }
395
+ };
396
+ node(undefined, true);
397
+ if (i < code.length) {
398
+ throw new IconValueError('unexpected text');
399
+ }
400
+ return sink.layers;
401
+ }
402
+ function readJson(json) {
403
+ const parsed = JSON.parse(json);
404
+ if (typeof parsed === 'string') {
405
+ return parsed.trim() ? [{ id: '1', icon: parsed }] : null;
406
+ }
407
+ const sink = new LayerSink();
408
+ const str = (v) => (typeof v === 'string' ? v : undefined);
409
+ const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : undefined);
410
+ const node = (value, parent, isBase) => {
411
+ var _a, _b;
412
+ if (!Array.isArray(value) || typeof value[0] !== 'string' || !value[0]) {
413
+ throw new IconValueError('node expected');
414
+ }
415
+ const [icon, ...rest] = value;
416
+ const first = rest[0];
417
+ const s = (first && typeof first === 'object' && !Array.isArray(first) ? rest.shift() : {});
418
+ const text = rest.filter((v) => typeof v === 'string').join('');
419
+ const size = num(s.s);
420
+ const id = sink.add({
421
+ icon,
422
+ variant: VARIANTS.get((_a = str(s.v)) !== null && _a !== void 0 ? _a : ''),
423
+ align: ALIGNS.get((_b = str(s.a)) !== null && _b !== void 0 ? _b : ''),
424
+ size: size === undefined ? undefined : Math.round(size * 1000) / 100000,
425
+ rotation: num(s.r),
426
+ offsetX: num(s.x),
427
+ offsetY: num(s.y),
428
+ primaryColor: str(s.p),
429
+ secondaryColor: str(s.sc),
430
+ accentColor: str(s.ac),
431
+ text: text || undefined,
432
+ textBinding: str(s.b),
433
+ textColor: str(s.tc),
434
+ textSize: num(s.ts),
435
+ }, parent, isBase);
436
+ for (const child of rest) {
437
+ if (Array.isArray(child)) {
438
+ node(child, id, false);
439
+ }
440
+ }
441
+ };
442
+ node(parsed, undefined, true);
443
+ return sink.layers;
444
+ }
445
+ /**
446
+ * Reads an icon kept as text (1.3.10) — a name, the code (`Folder(Star@tr)`), JSON or the HTML of
447
+ * NiceIconPicker 1.3.9 — into stack layers (ids `'1'`, `'2'`… in document order). `null` for empty
448
+ * or unreadable text. Names stay as written: `renderIconStack` resolves `Star` to `NtdStar`.
449
+ */
450
+ function parseIconValue(value) {
451
+ var _a, _b;
452
+ const text = value === null || value === void 0 ? void 0 : value.trim();
453
+ if (!text) {
454
+ return null;
455
+ }
456
+ try {
457
+ if (text.startsWith('<')) {
458
+ return (_b = (_a = parseIconHtml(text)) === null || _a === void 0 ? void 0 : _a.layers) !== null && _b !== void 0 ? _b : null;
459
+ }
460
+ if (text.startsWith('[') || text.startsWith('"')) {
461
+ return readJson(text);
462
+ }
463
+ return readCode(text);
464
+ }
465
+ catch (_c) {
466
+ return null;
467
+ }
468
+ }
469
+
470
+ export { formatIconValue, parseIconValue };
@@ -129,16 +129,19 @@ const NtdShare = /* @__PURE__ */ createIcon({
129
129
  // the bottom left as a line that stops 1 short of the front — closer than before. The gap is
130
130
  // worked out from the stroke width, so it stays the same at any weight; `sharp` draws the back
131
131
  // sheet with square corners (the derived one kept their arcs). Duotone, fills only: the back sheet
132
- // secondary under the primary front one; vivid: the back line secondary.
132
+ // secondary under the primary front one; vivid: the back line secondary. Round 40: the back sheet
133
+ // smaller, 11 (was 12.5), kept in the bottom-left corner.
133
134
  const COPY_FRONT = { x: 8.5, y: 3, width: 12.5, height: 12.5, rx: 2.5 };
134
- const COPY_BACK = { x: 3, y: 8.5, width: 12.5, height: 12.5, rx: 2.5 };
135
+ const COPY_BACK = { x: 3, y: 10, width: 11, height: 11, rx: 2.5 };
135
136
  /** What shows of the back sheet: its edges outside the front one, `gap` short of it. */
136
137
  function copyBack(gap, square = false) {
138
+ const { x, y, width, height, rx } = COPY_BACK;
137
139
  const top = COPY_FRONT.x - gap;
138
140
  const right = COPY_FRONT.y + COPY_FRONT.height + gap;
141
+ const arc = `a${rx} ${rx} 0 00`;
139
142
  return square
140
- ? `M${top} 8.5H3V21h12.5V${right}`
141
- : `M${top} 8.5H5.5a2.5 2.5 0 00-2.5 2.5v7.5a2.5 2.5 0 002.5 2.5h7.5a2.5 2.5 0 002.5-2.5V${right}`;
143
+ ? `M${top} ${y}H${x}V${y + height}H${x + width}V${right}`
144
+ : `M${top} ${y}H${x + rx}${arc}-${rx} ${rx}V${y + height - rx}${arc}${rx} ${rx}H${x + width - rx}${arc}${rx}-${rx}V${right}`;
142
145
  }
143
146
  /** The front sheet filled and the back one as a line 1 short of it (past its round cap). */
144
147
  function copyFilled(front, back, strokeWidth, square = false) {
@@ -342,15 +345,42 @@ const NtdUnlock = /* @__PURE__ */ createIcon({
342
345
  vivid: ({ primaryColor, secondaryColor }) => (jsxs(Fragment, { children: [jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2.5", fill: primaryColor }), jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2.5", fill: "white", opacity: "0.08" }), jsx("circle", { cx: "12", cy: "15.5", r: "1.5", fill: secondaryColor }), jsx("rect", { x: "11.25", y: "16.5", width: "1.5", height: "2", rx: "0.5", fill: "#3B82F6" }), jsx("path", { d: "M7 11V7a5 5 0 019.9-1", stroke: "white", strokeWidth: "1" })] })),
343
346
  },
344
347
  });
348
+ // The selection frame of four corners, shared by NtdFrameCorners and NtdGroup (round 40: the user
349
+ // asked for the frame as an icon of its own). A frame glyph: twice the stroke width in every variant
350
+ // but `outline`, mitred corners.
351
+ /** The top-left and bottom-right corners. */
352
+ const FRAME_CORNERS_MAIN = 'M3 7V3h4M21 17v4h-4';
353
+ /** The top-right and bottom-left corners. */
354
+ const FRAME_CORNERS_OTHER = 'M17 3h4v4M7 21H3v-4';
355
+ function frameCorners(main, other, width) {
356
+ return main === other ? (jsx("path", { d: `${FRAME_CORNERS_MAIN}${FRAME_CORNERS_OTHER}`, stroke: main, strokeWidth: width, strokeLinejoin: "miter", fill: "none" })) : (jsxs(Fragment, { children: [jsx("path", { d: FRAME_CORNERS_MAIN, stroke: main, strokeWidth: width, strokeLinejoin: "miter", fill: "none" }), jsx("path", { d: FRAME_CORNERS_OTHER, stroke: other, strokeWidth: width, strokeLinejoin: "miter", fill: "none" })] }));
357
+ }
358
+ // Duotone and vivid: two opposite corners primary, the other two secondary.
359
+ const NtdFrameCorners = /* @__PURE__ */ createIcon({
360
+ displayName: 'NtdFrameCorners',
361
+ paths: {
362
+ solid: ({ primaryColor, strokeWidth }) => frameCorners(primaryColor, primaryColor, strokeWidth * 2),
363
+ outline: ({ primaryColor, strokeWidth }) => frameCorners(primaryColor, primaryColor, strokeWidth),
364
+ duotone: ({ primaryColor, secondaryColor, strokeWidth }) => frameCorners(primaryColor, secondaryColor, strokeWidth * 2),
365
+ vivid: ({ primaryColor, secondaryColor, strokeWidth }) => frameCorners(primaryColor, secondaryColor, strokeWidth * 2),
366
+ },
367
+ });
345
368
  // Group / ungroup selected objects (1.2.2: the diagram toolbar drew them as ⊞ / ⊟).
346
369
  // Group: two objects inside the corners of one selection frame; ungroup: the two apart.
370
+ // Round 40: the frame is NtdFrameCorners' (2× the stroke width; the solid one was 1×, vivid's a
371
+ // fixed 2.2). Duotone, fills and the frame: the frame and the top-left object primary, the other
372
+ // object secondary; vivid puts the frame in the accent colour.
373
+ const GROUP_OBJECTS = ['M7 7h5v5H7z', 'M12 12h5v5h-5z'];
374
+ function groupFilled(frame, first, second, strokeWidth, frameOpacity) {
375
+ return (jsxs(Fragment, { children: [jsx("g", { opacity: frameOpacity, children: frameCorners(frame, frame, strokeWidth * 2) }), jsx("path", { d: GROUP_OBJECTS[0], fill: first }), jsx("path", { d: GROUP_OBJECTS[1], fill: second })] }));
376
+ }
347
377
  const NtdGroup = /* @__PURE__ */ createIcon({
348
378
  displayName: 'NtdGroup',
349
379
  paths: {
350
- solid: ({ primaryColor, strokeWidth }) => (jsxs(Fragment, { children: [jsx("path", { d: "M3 7V3h4M17 3h4v4M21 17v4h-4M7 21H3v-4", stroke: primaryColor, strokeWidth: strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", fill: "none" }), jsx("rect", { x: "7", y: "7", width: "5", height: "5", fill: primaryColor }), jsx("rect", { x: "12", y: "12", width: "5", height: "5", fill: primaryColor })] })),
351
- outline: ({ primaryColor, strokeWidth }) => (jsx("path", { d: "M3 7V3h4M17 3h4v4M21 17v4h-4M7 21H3v-4M7 7h5v5H7zM12 12h5v5h-5z", stroke: primaryColor, strokeWidth: strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", fill: "none" })),
352
- duotone: ({ primaryColor, secondaryColor, secondaryOpacity, strokeWidth }) => (jsxs(Fragment, { children: [jsx("rect", { x: "7", y: "7", width: "5", height: "5", fill: secondaryColor, opacity: secondaryOpacity }), jsx("rect", { x: "12", y: "12", width: "5", height: "5", fill: secondaryColor, opacity: secondaryOpacity }), jsx("path", { d: "M3 7V3h4M17 3h4v4M21 17v4h-4M7 21H3v-4M7 7h5v5H7zM12 12h5v5h-5z", stroke: primaryColor, strokeWidth: strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", fill: "none" })] })),
353
- vivid: ({ primaryColor, secondaryColor: _secondaryColor }) => (jsxs(Fragment, { children: [jsx("path", { d: "M3 7V3h4M17 3h4v4M21 17v4h-4M7 21H3v-4", stroke: primaryColor, strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", fill: "none" }), jsx("rect", { x: "7", y: "7", width: "5", height: "5", fill: primaryColor }), jsx("rect", { x: "12", y: "12", width: "5", height: "5", fill: primaryColor })] })),
380
+ solid: ({ primaryColor, strokeWidth }) => groupFilled(primaryColor, primaryColor, primaryColor, strokeWidth),
381
+ outline: ({ primaryColor, strokeWidth }) => (jsxs(Fragment, { children: [frameCorners(primaryColor, primaryColor, strokeWidth), jsx("path", { d: GROUP_OBJECTS.join(''), stroke: primaryColor, strokeWidth: strokeWidth, strokeLinejoin: "miter", fill: "none" })] })),
382
+ duotone: ({ primaryColor, secondaryColor, strokeWidth }) => groupFilled(primaryColor, primaryColor, secondaryColor, strokeWidth),
383
+ vivid: ({ primaryColor, secondaryColor, accentColor, accentOpacity, strokeWidth }) => groupFilled(accentColor, primaryColor, secondaryColor, strokeWidth, accentOpacity),
354
384
  },
355
385
  });
356
386
  const NtdUngroup = /* @__PURE__ */ createIcon({
@@ -363,4 +393,4 @@ const NtdUngroup = /* @__PURE__ */ createIcon({
363
393
  },
364
394
  });
365
395
 
366
- export { NtdCheck, NtdClipboard, NtdCopy, NtdDownload, NtdEdit, NtdEditDetail, NtdFilter, NtdGroup, NtdLink, NtdLock, NtdLogIn, NtdLogOut, NtdMinus, NtdPlus, NtdPrint, NtdRedo, NtdReply, NtdSave, NtdSearch, NtdSettings, NtdShare, NtdSort, NtdTrash, NtdUndo, NtdUngroup, NtdUnlock, NtdUpload, NtdZoomIn, NtdZoomOut };
396
+ export { NtdCheck, NtdClipboard, NtdCopy, NtdDownload, NtdEdit, NtdEditDetail, NtdFilter, NtdFrameCorners, NtdGroup, NtdLink, NtdLock, NtdLogIn, NtdLogOut, NtdMinus, NtdPlus, NtdPrint, NtdRedo, NtdReply, NtdSave, NtdSearch, NtdSettings, NtdShare, NtdSort, NtdTrash, NtdUndo, NtdUngroup, NtdUnlock, NtdUpload, NtdZoomIn, NtdZoomOut };
@@ -375,12 +375,33 @@ const NtdTarget = /* @__PURE__ */ createIcon({
375
375
  vivid: ({ primaryColor, secondaryColor }) => (jsxs(Fragment, { children: [jsx("circle", { cx: "12", cy: "12", r: "2", fill: primaryColor }), jsx("circle", { cx: "12", cy: "12", r: "2", fill: "white", opacity: "0.1" }), jsx("circle", { cx: "12", cy: "12", r: "10", stroke: secondaryColor, strokeWidth: "1.8", fill: "none" }), jsx("circle", { cx: "12", cy: "12", r: "6", stroke: "#3B82F6", strokeWidth: "1.5", fill: "none" })] })),
376
376
  },
377
377
  });
378
+ // NtdTruck, round 40: the filled variants all primary (the cab was a lighter primary with a stroke
379
+ // that also pushed its bottom 0.5 below the box's). The box and the cab are one shape, level at the
380
+ // bottom above the wheels — two shapes of one colour show a seam, and the box's rounded corner left
381
+ // a notch where it met the cab. Duotone and vivid on the theme's colours (vivid had fixed blue and
382
+ // amber wheels): the box primary, the cab secondary; the wheels primary with a ring — secondary in
383
+ // duotone, the accent in vivid, as in solid.
384
+ const TRUCK_BODY = 'M2 3h13a1 1 0 011 1v4h4l3 3v5H2a1 1 0 01-1-1V4a1 1 0 011-1z';
385
+ /** The body with `rounded`'s corners — the derived variant cannot round a path. */
386
+ const TRUCK_BODY_ROUNDED = 'M4 3h9a3 3 0 013 3v2h4l3 3v3a2 2 0 01-2 2H4a3 3 0 01-3-3V6a3 3 0 013-3z';
387
+ /** Duotone and vivid: the box, square where the cab covers it, and the cab 0.5 over it. */
388
+ const TRUCK_BOX = 'M2 3h13a1 1 0 011 1v12H2a1 1 0 01-1-1V4a1 1 0 011-1z';
389
+ const TRUCK_CAB = 'M15.5 8H20l3 3v5h-7.5z';
390
+ const TRUCK_WHEELS = [5.5, 18.5];
391
+ function truckWheels(fill, ring, opacity) {
392
+ return TRUCK_WHEELS.map((cx) => (jsx("circle", { cx: cx, cy: "18.5", r: "2.5", fill: fill, stroke: ring, strokeWidth: "1.5", opacity: opacity }, cx)));
393
+ }
394
+ function truckSolid(body, { primaryColor, accentColor, accentOpacity }) {
395
+ return (jsxs(Fragment, { children: [jsx("path", { d: body, fill: primaryColor }), truckWheels(primaryColor, accentColor, accentOpacity)] }));
396
+ }
378
397
  const NtdTruck = /* @__PURE__ */ createIcon({
379
398
  displayName: 'NtdTruck',
380
399
  paths: {
381
- solid: ({ primaryColor, accentColor, accentOpacity, strokeWidth }) => (jsxs(Fragment, { children: [jsx("rect", { x: "1", y: "3", width: "15", height: "13", rx: "1", fill: primaryColor }), jsx("path", { d: "M16 8h4l3 3v5h-7V8z", fill: primaryColor, opacity: "0.7", stroke: primaryColor, strokeWidth: strokeWidth }), jsx("circle", { cx: "5.5", cy: "18.5", r: "2.5", fill: primaryColor, stroke: accentColor, strokeWidth: "1.5", opacity: accentOpacity }), jsx("circle", { cx: "18.5", cy: "18.5", r: "2.5", fill: primaryColor, stroke: accentColor, strokeWidth: "1.5", opacity: accentOpacity })] })),
400
+ solid: (props) => truckSolid(TRUCK_BODY, props),
401
+ rounded: (props) => truckSolid(TRUCK_BODY_ROUNDED, props),
382
402
  outline: ({ primaryColor, strokeWidth }) => (jsxs(Fragment, { children: [jsx("path", { d: "M1 3h15v13H1z", stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" }), jsx("path", { d: "M16 8h4l3 3v5h-7V8z", stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" }), jsx("circle", { cx: "5.5", cy: "18.5", r: "2.5", stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" }), jsx("circle", { cx: "18.5", cy: "18.5", r: "2.5", stroke: primaryColor, strokeWidth: strokeWidth, fill: "none" })] })),
383
- vivid: ({ primaryColor, secondaryColor }) => (jsxs(Fragment, { children: [jsx("rect", { x: "1", y: "3", width: "15", height: "13", rx: "1", fill: primaryColor }), jsx("rect", { x: "1", y: "3", width: "15", height: "13", rx: "1", fill: "white", opacity: "0.08" }), jsx("path", { d: "M16 8h4l3 3v5h-7V8z", fill: secondaryColor, opacity: "0.7", stroke: secondaryColor, strokeWidth: "2" }), jsx("circle", { cx: "5.5", cy: "18.5", r: "2.5", fill: "#3B82F6", stroke: "#3B82F6", strokeWidth: "1.5" }), jsx("circle", { cx: "18.5", cy: "18.5", r: "2.5", fill: "#F59E0B", stroke: "#F59E0B", strokeWidth: "1.5" })] })),
403
+ duotone: ({ primaryColor, secondaryColor, secondaryOpacity }) => (jsxs(Fragment, { children: [jsx("path", { d: TRUCK_BOX, fill: primaryColor }), jsx("path", { d: TRUCK_CAB, fill: secondaryColor, opacity: secondaryOpacity }), truckWheels(primaryColor, secondaryColor)] })),
404
+ vivid: ({ primaryColor, secondaryColor, accentColor, accentOpacity }) => (jsxs(Fragment, { children: [jsx("path", { d: TRUCK_BOX, fill: primaryColor }), jsx("path", { d: TRUCK_CAB, fill: secondaryColor }), truckWheels(primaryColor, accentColor, accentOpacity)] })),
384
405
  },
385
406
  });
386
407