@ship-ui/core 0.19.2 → 0.19.4
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/assets/mcp/components.json +2587 -2515
- package/bin/mcp/index.js +14 -3
- package/bin/ship-fg-scanner +0 -0
- package/bin/ship-fg.mjs +116 -8
- package/bin/src/ship-fg.ts +120 -8
- package/fesm2022/ship-ui-core.mjs +513 -126
- package/fesm2022/ship-ui-core.mjs.map +1 -1
- package/package.json +2 -1
- package/styles/components/ship-datepicker.scss +35 -3
- package/styles/components/ship-form-field.scss +1 -0
- package/types/ship-ui-core.d.ts +108 -72
- package/snippets/ship-ui.code-snippets +0 -536
package/bin/mcp/index.js
CHANGED
|
@@ -13684,13 +13684,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
13684
13684
|
const { name, arguments: args } = request.params;
|
|
13685
13685
|
if (name === "search_components") {
|
|
13686
13686
|
const { query } = args;
|
|
13687
|
-
const results = components.filter((c) =>
|
|
13687
|
+
const results = components.filter((c) => {
|
|
13688
|
+
const q = query.toLowerCase();
|
|
13689
|
+
return c.name.toLowerCase().includes(q) || c.selector.toLowerCase().includes(q) || c.keywords?.some((k) => k.toLowerCase().includes(q));
|
|
13690
|
+
});
|
|
13688
13691
|
return {
|
|
13689
13692
|
content: [
|
|
13690
13693
|
{
|
|
13691
13694
|
type: "text",
|
|
13692
|
-
text: JSON.stringify(results.map((c) => ({
|
|
13693
|
-
|
|
13695
|
+
text: JSON.stringify(results.map((c) => ({
|
|
13696
|
+
name: c.name,
|
|
13697
|
+
selector: c.selector,
|
|
13698
|
+
description: c.description?.split(`
|
|
13699
|
+
`)[0],
|
|
13700
|
+
keywords: c.keywords
|
|
13701
|
+
})), null, 2)
|
|
13694
13702
|
}
|
|
13695
13703
|
]
|
|
13696
13704
|
};
|
|
@@ -13760,6 +13768,9 @@ Here are the component details:
|
|
|
13760
13768
|
Description:
|
|
13761
13769
|
${component.description || "No description available"}
|
|
13762
13770
|
|
|
13771
|
+
Keywords:
|
|
13772
|
+
${component.keywords?.join(", ") || "None"}
|
|
13773
|
+
|
|
13763
13774
|
Inputs:
|
|
13764
13775
|
${component.inputs.map((i) => `- ${i.name} (${i.type})${i.defaultValue ? ` (default: ${i.defaultValue})` : ""}${i.options ? ` [options: ${i.options.join(", ")}]` : ""}${i.description ? `: ${i.description}` : ""}`).join(`
|
|
13765
13776
|
`)}
|
package/bin/ship-fg-scanner
CHANGED
|
Binary file
|
package/bin/ship-fg.mjs
CHANGED
|
@@ -3235,7 +3235,7 @@ import { parseArgs } from "util";
|
|
|
3235
3235
|
// bin/src/ship-fg.ts
|
|
3236
3236
|
import { spawnSync } from "child_process";
|
|
3237
3237
|
import { watch } from "fs";
|
|
3238
|
-
import { readFile as readFile2, writeFile } from "fs/promises";
|
|
3238
|
+
import { readFile as readFile2, writeFile, readdir, mkdir } from "fs/promises";
|
|
3239
3239
|
import { dirname, join, resolve } from "path";
|
|
3240
3240
|
import { fileURLToPath } from "url";
|
|
3241
3241
|
import { gzipSync } from "zlib";
|
|
@@ -3326,6 +3326,104 @@ function formatFileSize(bytes, dm = 2) {
|
|
|
3326
3326
|
var _dirname = dirname(fileURLToPath(import.meta.url));
|
|
3327
3327
|
var CWD_PATH = process.cwd();
|
|
3328
3328
|
var PHOSPHOR_SRC_PATH = resolve(CWD_PATH, "node_modules", "@phosphor-icons", "web", "src");
|
|
3329
|
+
var jsScannerFallback = async (PROJECT_SRC, shipUiDir, CWD_PATH2) => {
|
|
3330
|
+
const uniqueIcons = new Set;
|
|
3331
|
+
const isValidIcon = (s) => /^[a-z0-9-]+$/.test(s);
|
|
3332
|
+
try {
|
|
3333
|
+
const pkgPath = resolve(shipUiDir, "package.json");
|
|
3334
|
+
const pkgData = JSON.parse(await readFile2(pkgPath, "utf8"));
|
|
3335
|
+
if (pkgData.libraryIcons) {
|
|
3336
|
+
pkgData.libraryIcons.forEach((i) => uniqueIcons.add(i));
|
|
3337
|
+
}
|
|
3338
|
+
} catch (e) {}
|
|
3339
|
+
async function scanDir(dir) {
|
|
3340
|
+
const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
3341
|
+
for (const entry of entries) {
|
|
3342
|
+
if (entry.name === "node_modules" || entry.name.startsWith("."))
|
|
3343
|
+
continue;
|
|
3344
|
+
const fullPath = resolve(dir, entry.name);
|
|
3345
|
+
if (entry.isDirectory()) {
|
|
3346
|
+
await scanDir(fullPath);
|
|
3347
|
+
} else if (entry.name.endsWith(".html") || entry.name.endsWith(".ts")) {
|
|
3348
|
+
const contents = await readFile2(fullPath, "utf8");
|
|
3349
|
+
const htmlStartMatches = contents.matchAll(/<sh-icon[^>]*icon=['"]([^'"]+)['"][^>]*>/g);
|
|
3350
|
+
for (const m of htmlStartMatches) {
|
|
3351
|
+
const icon = m[1].trim();
|
|
3352
|
+
if (isValidIcon(icon))
|
|
3353
|
+
uniqueIcons.add(icon);
|
|
3354
|
+
}
|
|
3355
|
+
const htmlContentMatches = contents.matchAll(/<sh-icon[^>]*>([^<]+)<\/sh-icon>/g);
|
|
3356
|
+
for (const m of htmlContentMatches) {
|
|
3357
|
+
const icon = m[1].trim();
|
|
3358
|
+
if (isValidIcon(icon))
|
|
3359
|
+
uniqueIcons.add(icon);
|
|
3360
|
+
}
|
|
3361
|
+
const tsMatches = contents.matchAll(/shicon:([^'"]+)['"]/g);
|
|
3362
|
+
for (const m of tsMatches) {
|
|
3363
|
+
const icon = m[1].trim();
|
|
3364
|
+
if (isValidIcon(icon))
|
|
3365
|
+
uniqueIcons.add(icon);
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
await scanDir(PROJECT_SRC);
|
|
3371
|
+
const variants = ["bold", "thin", "light", "fill", "regular", "duotone"];
|
|
3372
|
+
const glyphMaps = new Map;
|
|
3373
|
+
for (const variant of variants) {
|
|
3374
|
+
try {
|
|
3375
|
+
const selPath = resolve(CWD_PATH2, "node_modules", "@phosphor-icons", "web", "src", variant, "selection.json");
|
|
3376
|
+
const parsed = JSON.parse(await readFile2(selPath, "utf8"));
|
|
3377
|
+
const isDuotone = variant === "duotone";
|
|
3378
|
+
for (const icon of parsed.icons || []) {
|
|
3379
|
+
const hexCode = icon.properties.code.toString(16);
|
|
3380
|
+
const unicodeStr = `U+${hexCode}`;
|
|
3381
|
+
const glyph = String.fromCodePoint(icon.properties.code);
|
|
3382
|
+
let glyphName = isDuotone ? icon.properties.name : icon.properties.ligatures;
|
|
3383
|
+
if (glyphName)
|
|
3384
|
+
glyphMaps.set(glyphName, [glyph, unicodeStr]);
|
|
3385
|
+
}
|
|
3386
|
+
} catch (e) {}
|
|
3387
|
+
}
|
|
3388
|
+
const grouped = {
|
|
3389
|
+
bold: [],
|
|
3390
|
+
thin: [],
|
|
3391
|
+
light: [],
|
|
3392
|
+
fill: [],
|
|
3393
|
+
regular: [],
|
|
3394
|
+
duotone: [],
|
|
3395
|
+
text: [],
|
|
3396
|
+
missing: []
|
|
3397
|
+
};
|
|
3398
|
+
for (const icon of uniqueIcons) {
|
|
3399
|
+
const isBold = icon.endsWith("-bold");
|
|
3400
|
+
const isThin = icon.endsWith("-thin");
|
|
3401
|
+
const isLight = icon.endsWith("-light");
|
|
3402
|
+
const isFill = icon.endsWith("-fill");
|
|
3403
|
+
const isDuotone = icon.endsWith("-duotone");
|
|
3404
|
+
const isRegular = !isBold && !isThin && !isLight && !isFill && !isDuotone;
|
|
3405
|
+
const mapEntry = glyphMaps.get(icon);
|
|
3406
|
+
if (mapEntry) {
|
|
3407
|
+
const tuple1 = [icon, ""];
|
|
3408
|
+
const tuple2 = mapEntry;
|
|
3409
|
+
if (isBold)
|
|
3410
|
+
grouped.bold.push(tuple1, tuple2);
|
|
3411
|
+
else if (isThin)
|
|
3412
|
+
grouped.thin.push(tuple1, tuple2);
|
|
3413
|
+
else if (isLight)
|
|
3414
|
+
grouped.light.push(tuple1, tuple2);
|
|
3415
|
+
else if (isFill)
|
|
3416
|
+
grouped.fill.push(tuple1, tuple2);
|
|
3417
|
+
else if (isDuotone)
|
|
3418
|
+
grouped.duotone.push(tuple1, tuple2);
|
|
3419
|
+
else
|
|
3420
|
+
grouped.regular.push(tuple1, tuple2);
|
|
3421
|
+
} else {
|
|
3422
|
+
grouped.missing.push(icon);
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
return grouped;
|
|
3426
|
+
};
|
|
3329
3427
|
var writtenCssSize = 0;
|
|
3330
3428
|
var compressedCssSize = 0;
|
|
3331
3429
|
var watchers = [];
|
|
@@ -3339,15 +3437,20 @@ var run = async (PROJECT_SRC, PROJECT_PUBLIC, TARGET_FONT_TYPE, values) => {
|
|
|
3339
3437
|
try {
|
|
3340
3438
|
const proc = spawnSync(scannerPath, [PROJECT_SRC, shipUiDir, CWD_PATH]);
|
|
3341
3439
|
if (proc.error || proc.status !== 0) {
|
|
3342
|
-
console.error("Error running scanner:", proc.stderr?.toString() || proc.error);
|
|
3343
3440
|
throw new Error("Native scanner failed");
|
|
3344
3441
|
}
|
|
3345
|
-
const
|
|
3346
|
-
groupedIcons =
|
|
3347
|
-
missingIconsArray =
|
|
3442
|
+
const { missing, ...rest } = JSON.parse(proc.stdout?.toString() || "{}");
|
|
3443
|
+
groupedIcons = rest;
|
|
3444
|
+
missingIconsArray = missing || [];
|
|
3348
3445
|
} catch (err) {
|
|
3349
|
-
|
|
3350
|
-
|
|
3446
|
+
try {
|
|
3447
|
+
const { missing, ...rest } = await jsScannerFallback(PROJECT_SRC, shipUiDir, CWD_PATH);
|
|
3448
|
+
groupedIcons = rest;
|
|
3449
|
+
missingIconsArray = missing || [];
|
|
3450
|
+
} catch (fallbackErr) {
|
|
3451
|
+
console.error("Failed to run high-performance zig scanner and the javascript fallback failed:", fallbackErr);
|
|
3452
|
+
throw err;
|
|
3453
|
+
}
|
|
3351
3454
|
}
|
|
3352
3455
|
if (missingIconsArray.length) {
|
|
3353
3456
|
console.log(`Following icons does not exist in font:
|
|
@@ -3473,7 +3576,12 @@ var textMateSnippet = async (GLYPH_MAP) => {
|
|
|
3473
3576
|
}
|
|
3474
3577
|
}
|
|
3475
3578
|
`;
|
|
3476
|
-
|
|
3579
|
+
try {
|
|
3580
|
+
await mkdir("./.vscode", { recursive: true });
|
|
3581
|
+
await writeFile("./.vscode/html.code-snippets", iconsSnippetContent);
|
|
3582
|
+
} catch (error) {
|
|
3583
|
+
console.warn("⚠️ Could not generate VS Code snippets:", error);
|
|
3584
|
+
}
|
|
3477
3585
|
};
|
|
3478
3586
|
function capitalize(str) {
|
|
3479
3587
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
package/bin/src/ship-fg.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { spawnSync } from 'child_process';
|
|
4
4
|
import { FSWatcher, watch } from 'fs';
|
|
5
|
-
import { readFile, writeFile } from 'fs/promises';
|
|
5
|
+
import { readFile, writeFile, readdir, mkdir } from 'fs/promises';
|
|
6
6
|
import { dirname, join, resolve } from 'path';
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import { gzipSync } from 'zlib';
|
|
@@ -16,6 +16,107 @@ import { formatFileSize, InputArguments, SupportedFontTypes } from './utilities'
|
|
|
16
16
|
const CWD_PATH = process.cwd();
|
|
17
17
|
const PHOSPHOR_SRC_PATH = resolve(CWD_PATH, 'node_modules', '@phosphor-icons', 'web', 'src');
|
|
18
18
|
|
|
19
|
+
const jsScannerFallback = async (PROJECT_SRC: string, shipUiDir: string, CWD_PATH: string) => {
|
|
20
|
+
const uniqueIcons = new Set<string>();
|
|
21
|
+
|
|
22
|
+
const isValidIcon = (s: string) => /^[a-z0-9-]+$/.test(s);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const pkgPath = resolve(shipUiDir, 'package.json');
|
|
26
|
+
const pkgData = JSON.parse(await readFile(pkgPath, 'utf8'));
|
|
27
|
+
if (pkgData.libraryIcons) {
|
|
28
|
+
pkgData.libraryIcons.forEach((i: string) => uniqueIcons.add(i));
|
|
29
|
+
}
|
|
30
|
+
} catch (e) {}
|
|
31
|
+
|
|
32
|
+
async function scanDir(dir: string) {
|
|
33
|
+
const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
34
|
+
for (const entry of entries) {
|
|
35
|
+
if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue;
|
|
36
|
+
const fullPath = resolve(dir, entry.name);
|
|
37
|
+
if (entry.isDirectory()) {
|
|
38
|
+
await scanDir(fullPath);
|
|
39
|
+
} else if (entry.name.endsWith('.html') || entry.name.endsWith('.ts')) {
|
|
40
|
+
const contents = await readFile(fullPath, 'utf8');
|
|
41
|
+
|
|
42
|
+
const htmlStartMatches = contents.matchAll(/<sh-icon[^>]*icon=['"]([^'"]+)['"][^>]*>/g);
|
|
43
|
+
for (const m of htmlStartMatches) {
|
|
44
|
+
const icon = m[1].trim();
|
|
45
|
+
if (isValidIcon(icon)) uniqueIcons.add(icon);
|
|
46
|
+
}
|
|
47
|
+
const htmlContentMatches = contents.matchAll(/<sh-icon[^>]*>([^<]+)<\/sh-icon>/g);
|
|
48
|
+
for (const m of htmlContentMatches) {
|
|
49
|
+
const icon = m[1].trim();
|
|
50
|
+
if (isValidIcon(icon)) uniqueIcons.add(icon);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const tsMatches = contents.matchAll(/shicon:([^'"]+)['"]/g);
|
|
54
|
+
for (const m of tsMatches) {
|
|
55
|
+
const icon = m[1].trim();
|
|
56
|
+
if (isValidIcon(icon)) uniqueIcons.add(icon);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
await scanDir(PROJECT_SRC);
|
|
62
|
+
|
|
63
|
+
const variants = ['bold', 'thin', 'light', 'fill', 'regular', 'duotone'];
|
|
64
|
+
const glyphMaps = new Map<string, [string, string]>();
|
|
65
|
+
|
|
66
|
+
for (const variant of variants) {
|
|
67
|
+
try {
|
|
68
|
+
const selPath = resolve(CWD_PATH, 'node_modules', '@phosphor-icons', 'web', 'src', variant, 'selection.json');
|
|
69
|
+
const parsed = JSON.parse(await readFile(selPath, 'utf8'));
|
|
70
|
+
const isDuotone = variant === 'duotone';
|
|
71
|
+
|
|
72
|
+
for (const icon of parsed.icons || []) {
|
|
73
|
+
const hexCode = icon.properties.code.toString(16);
|
|
74
|
+
const unicodeStr = `U+${hexCode}`;
|
|
75
|
+
const glyph = String.fromCodePoint(icon.properties.code);
|
|
76
|
+
|
|
77
|
+
let glyphName = isDuotone ? icon.properties.name : icon.properties.ligatures;
|
|
78
|
+
if (glyphName) glyphMaps.set(glyphName, [glyph, unicodeStr]);
|
|
79
|
+
}
|
|
80
|
+
} catch (e) {}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const grouped = {
|
|
84
|
+
bold: [] as [string, string][],
|
|
85
|
+
thin: [] as [string, string][],
|
|
86
|
+
light: [] as [string, string][],
|
|
87
|
+
fill: [] as [string, string][],
|
|
88
|
+
regular: [] as [string, string][],
|
|
89
|
+
duotone: [] as [string, string][],
|
|
90
|
+
text: [] as [string, string][],
|
|
91
|
+
missing: [] as string[],
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
for (const icon of uniqueIcons) {
|
|
95
|
+
const isBold = icon.endsWith('-bold');
|
|
96
|
+
const isThin = icon.endsWith('-thin');
|
|
97
|
+
const isLight = icon.endsWith('-light');
|
|
98
|
+
const isFill = icon.endsWith('-fill');
|
|
99
|
+
const isDuotone = icon.endsWith('-duotone');
|
|
100
|
+
const isRegular = !isBold && !isThin && !isLight && !isFill && !isDuotone;
|
|
101
|
+
|
|
102
|
+
const mapEntry = glyphMaps.get(icon);
|
|
103
|
+
if (mapEntry) {
|
|
104
|
+
const tuple1: [string, string] = [icon, ''];
|
|
105
|
+
const tuple2 = mapEntry; // [glyph, unicodeStr]
|
|
106
|
+
if (isBold) grouped.bold.push(tuple1, tuple2);
|
|
107
|
+
else if (isThin) grouped.thin.push(tuple1, tuple2);
|
|
108
|
+
else if (isLight) grouped.light.push(tuple1, tuple2);
|
|
109
|
+
else if (isFill) grouped.fill.push(tuple1, tuple2);
|
|
110
|
+
else if (isDuotone) grouped.duotone.push(tuple1, tuple2);
|
|
111
|
+
else grouped.regular.push(tuple1, tuple2);
|
|
112
|
+
} else {
|
|
113
|
+
grouped.missing.push(icon);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return grouped;
|
|
118
|
+
};
|
|
119
|
+
|
|
19
120
|
let writtenCssSize = 0;
|
|
20
121
|
let compressedCssSize = 0;
|
|
21
122
|
let watchers: FSWatcher[] = [];
|
|
@@ -37,16 +138,21 @@ const run = async (
|
|
|
37
138
|
try {
|
|
38
139
|
const proc = spawnSync(scannerPath, [PROJECT_SRC, shipUiDir, CWD_PATH]);
|
|
39
140
|
if (proc.error || proc.status !== 0) {
|
|
40
|
-
console.error('Error running scanner:', proc.stderr?.toString() || proc.error);
|
|
41
141
|
throw new Error('Native scanner failed');
|
|
42
142
|
}
|
|
43
143
|
|
|
44
|
-
const
|
|
45
|
-
groupedIcons =
|
|
46
|
-
missingIconsArray =
|
|
144
|
+
const { missing, ...rest } = JSON.parse(proc.stdout?.toString() || '{}');
|
|
145
|
+
groupedIcons = rest;
|
|
146
|
+
missingIconsArray = missing || [];
|
|
47
147
|
} catch (err) {
|
|
48
|
-
|
|
49
|
-
|
|
148
|
+
try {
|
|
149
|
+
const { missing, ...rest } = await jsScannerFallback(PROJECT_SRC, shipUiDir, CWD_PATH);
|
|
150
|
+
groupedIcons = rest;
|
|
151
|
+
missingIconsArray = missing || [];
|
|
152
|
+
} catch (fallbackErr) {
|
|
153
|
+
console.error('Failed to run high-performance zig scanner and the javascript fallback failed:', fallbackErr);
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
50
156
|
}
|
|
51
157
|
|
|
52
158
|
if (missingIconsArray.length) {
|
|
@@ -203,7 +309,13 @@ const textMateSnippet = async (GLYPH_MAP: Record<string, [string, string]>) => {
|
|
|
203
309
|
}
|
|
204
310
|
`;
|
|
205
311
|
|
|
206
|
-
|
|
312
|
+
try {
|
|
313
|
+
await mkdir('./.vscode', { recursive: true });
|
|
314
|
+
await writeFile('./.vscode/html.code-snippets', iconsSnippetContent);
|
|
315
|
+
} catch (error) {
|
|
316
|
+
// Gracefully ignore snippet generation failures (e.g. in read-only CI environments)
|
|
317
|
+
console.warn('⚠️ Could not generate VS Code snippets:', error);
|
|
318
|
+
}
|
|
207
319
|
};
|
|
208
320
|
|
|
209
321
|
function capitalize(str: string) {
|