@lism-css/mcp 0.1.0 → 0.2.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/dist/data/components.json +243 -86
- package/dist/data/docs-index.json +211 -237
- package/dist/data/meta.js +2 -2
- package/dist/data/overview.json +52 -18
- package/dist/data/props-system.json +563 -27
- package/dist/data/tokens.json +70 -69
- package/dist/index.js +2 -0
- package/dist/lib/schemas.d.ts +3 -0
- package/dist/lib/schemas.js +1 -0
- package/dist/lib/search.d.ts +21 -2
- package/dist/lib/search.js +79 -4
- package/dist/lib/types.d.ts +1 -0
- package/dist/tools/convert-css.d.ts +2 -0
- package/dist/tools/convert-css.js +223 -0
- package/dist/tools/get-component.js +26 -7
- package/dist/tools/get-overview.js +4 -1
- package/dist/tools/get-props-system.js +32 -7
- package/dist/tools/get-tokens.js +7 -3
- package/dist/tools/search-docs.js +16 -8
- package/package.json +6 -6
package/dist/data/tokens.json
CHANGED
|
@@ -3,119 +3,120 @@
|
|
|
3
3
|
"category": "color",
|
|
4
4
|
"description": "セマンティックカラートークン。サイト全体のカラー設計に使用。",
|
|
5
5
|
"tokens": [
|
|
6
|
-
{ "name": "--base", "value": "
|
|
7
|
-
{ "name": "--base-2", "value": "
|
|
8
|
-
{ "name": "--text", "value": "
|
|
9
|
-
{ "name": "--text-2", "value": "
|
|
10
|
-
{ "name": "--divider", "value": "
|
|
11
|
-
{ "name": "--link", "value": "
|
|
12
|
-
{ "name": "--brand", "value": "
|
|
13
|
-
{ "name": "--accent", "value": "
|
|
6
|
+
{ "name": "--base", "value": "hsl(224 4% 99%)", "description": "ベース背景色" },
|
|
7
|
+
{ "name": "--base-2", "value": "hsl(224 8% 95%)", "description": "代替背景色" },
|
|
8
|
+
{ "name": "--text", "value": "hsl(224 4% 8%)", "description": "基本テキスト色" },
|
|
9
|
+
{ "name": "--text-2", "value": "hsl(224 6% 32%)", "description": "補助テキスト色" },
|
|
10
|
+
{ "name": "--divider", "value": "hsl(224 8% 88%)", "description": "区切り線色" },
|
|
11
|
+
{ "name": "--link", "value": "oklch(50% 0.3 240)", "description": "リンク色" },
|
|
12
|
+
{ "name": "--brand", "value": "#1e5f8c", "description": "ブランド色" },
|
|
13
|
+
{ "name": "--accent", "value": "#d94a6a", "description": "アクセント色" }
|
|
14
14
|
]
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"category": "color",
|
|
18
|
-
"description": "
|
|
18
|
+
"description": "パレットカラートークン。OKLCHで定義。--L(明度)と--C(彩度)の基準値を調整するとパレット全体の色味を変更可能。",
|
|
19
19
|
"tokens": [
|
|
20
|
-
{ "name": "--red", "value": "
|
|
21
|
-
{ "name": "--
|
|
22
|
-
{ "name": "--
|
|
23
|
-
{ "name": "--
|
|
24
|
-
{ "name": "--
|
|
25
|
-
{ "name": "--
|
|
26
|
-
{ "name": "--pink", "value": "
|
|
27
|
-
{ "name": "--gray", "value": "
|
|
28
|
-
{ "name": "--
|
|
29
|
-
{ "name": "--
|
|
20
|
+
{ "name": "--red", "value": "oklch(var(--L) var(--C) 20)" },
|
|
21
|
+
{ "name": "--orange", "value": "oklch(calc(var(--L) + 4%) calc(var(--C) - 0.01) 52)" },
|
|
22
|
+
{ "name": "--yellow", "value": "oklch(calc(var(--L) + 12%) calc(var(--C) - 0.025) 84)" },
|
|
23
|
+
{ "name": "--green", "value": "oklch(calc(var(--L) + 4%) calc(var(--C) - 0.02) 152)" },
|
|
24
|
+
{ "name": "--blue", "value": "oklch(calc(var(--L) - 2%) calc(var(--C) + 0.01) 260)" },
|
|
25
|
+
{ "name": "--purple", "value": "oklch(calc(var(--L) - 4%) calc(var(--C) + 0.02) 292)" },
|
|
26
|
+
{ "name": "--pink", "value": "oklch(calc(var(--L) + 2%) calc(var(--C) + 0.02) 348)" },
|
|
27
|
+
{ "name": "--gray", "value": "oklch(calc(var(--L) - 4%) 0.04 256)" },
|
|
28
|
+
{ "name": "--black", "value": "#000" },
|
|
29
|
+
{ "name": "--white", "value": "#fff" },
|
|
30
30
|
{ "name": "--keycolor", "value": "コンポーネントごとのキーカラー" }
|
|
31
31
|
]
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
"category": "spacing",
|
|
35
|
-
"description": "スペーシングトークン。padding, margin, gap 等で使用。CSS変数名は --s{値}
|
|
35
|
+
"description": "スペーシングトークン。padding, margin, gap 等で使用。CSS変数名は --s{値}。フィボナッチ数列ベースのスケーリング。--s-unit(≒8px)を基準とする。",
|
|
36
36
|
"tokens": [
|
|
37
|
-
{ "name": "--s5", "value": "
|
|
38
|
-
{ "name": "--s10", "value": "
|
|
39
|
-
{ "name": "--s15", "value": "
|
|
40
|
-
{ "name": "--s20", "value": "
|
|
41
|
-
{ "name": "--s30", "value": "
|
|
42
|
-
{ "name": "--s40", "value": "
|
|
43
|
-
{ "name": "--s50", "value": "
|
|
44
|
-
{ "name": "--s60", "value": "
|
|
45
|
-
{ "name": "--s70", "value": "
|
|
46
|
-
{ "name": "--s80", "value": "
|
|
37
|
+
{ "name": "--s5", "value": "calc(0.5 * var(--s-unit))", "description": "≒ 4px" },
|
|
38
|
+
{ "name": "--s10", "value": "var(--s-unit)", "description": "≒ 8px" },
|
|
39
|
+
{ "name": "--s15", "value": "calc(1.5 * var(--s-unit))", "description": "≒ 12px" },
|
|
40
|
+
{ "name": "--s20", "value": "calc(2 * var(--s-unit))", "description": "≒ 16px" },
|
|
41
|
+
{ "name": "--s30", "value": "calc(3 * var(--s-unit))", "description": "≒ 24px" },
|
|
42
|
+
{ "name": "--s40", "value": "calc(5 * var(--s-unit))", "description": "≒ 40px" },
|
|
43
|
+
{ "name": "--s50", "value": "calc(8 * var(--s-unit))", "description": "≒ 64px" },
|
|
44
|
+
{ "name": "--s60", "value": "calc(13 * var(--s-unit))", "description": "≒ 104px" },
|
|
45
|
+
{ "name": "--s70", "value": "calc(21 * var(--s-unit))", "description": "≒ 168px" },
|
|
46
|
+
{ "name": "--s80", "value": "calc(34 * var(--s-unit))", "description": "≒ 272px" }
|
|
47
47
|
]
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"category": "fontSize",
|
|
51
|
-
"description": "フォントサイズトークン。CSS変数名は --fz--{値}
|
|
51
|
+
"description": "フォントサイズトークン。CSS変数名は --fz--{値}。分子8の調和数列(倍音列)ベースのスケーリング。",
|
|
52
52
|
"tokens": [
|
|
53
|
-
{ "name": "--fz--
|
|
54
|
-
{ "name": "--fz--
|
|
55
|
-
{ "name": "--fz--
|
|
56
|
-
{ "name": "--fz--
|
|
57
|
-
{ "name": "--fz--
|
|
58
|
-
{ "name": "--fz--
|
|
59
|
-
{ "name": "--fz--
|
|
60
|
-
{ "name": "--fz--
|
|
61
|
-
{ "name": "--fz--
|
|
62
|
-
{ "name": "--fz--
|
|
63
|
-
{ "name": "--fz--
|
|
64
|
-
{ "name": "--fz--2xs", "value": "最小サイズ" }
|
|
53
|
+
{ "name": "--fz--base", "value": "var(--REM)", "description": "本文の基本フォントサイズ(≒ 1rem)" },
|
|
54
|
+
{ "name": "--fz--5xl", "value": "4em", "description": "最大サイズ(8/2)" },
|
|
55
|
+
{ "name": "--fz--4xl", "value": "2.667em", "description": "特大サイズ(8/3)" },
|
|
56
|
+
{ "name": "--fz--3xl", "value": "2em", "description": "3XLサイズ(8/4)" },
|
|
57
|
+
{ "name": "--fz--2xl", "value": "1.6em", "description": "2XLサイズ(8/5)" },
|
|
58
|
+
{ "name": "--fz--xl", "value": "1.333em", "description": "XLサイズ(8/6)" },
|
|
59
|
+
{ "name": "--fz--l", "value": "1.143em", "description": "Lサイズ(8/7)" },
|
|
60
|
+
{ "name": "--fz--m", "value": "1em", "description": "Mサイズ(基準)" },
|
|
61
|
+
{ "name": "--fz--s", "value": "0.889em", "description": "Sサイズ(8/9)" },
|
|
62
|
+
{ "name": "--fz--xs", "value": "0.8em", "description": "XSサイズ(8/10)" },
|
|
63
|
+
{ "name": "--fz--2xs", "value": "0.727em", "description": "最小サイズ(8/11)" }
|
|
65
64
|
]
|
|
66
65
|
},
|
|
67
66
|
{
|
|
68
67
|
"category": "lineHeight",
|
|
69
|
-
"description": "
|
|
68
|
+
"description": "行間(ハーフレディング)トークン。CSS変数名は --hl--{値}。line-heightは calc(1em + var(--hl) * 2) で算出される。--hl-unit(≒2px)を基準とする。",
|
|
70
69
|
"tokens": [
|
|
71
|
-
{ "name": "--hl--
|
|
72
|
-
{ "name": "--hl--
|
|
73
|
-
{ "name": "--hl--
|
|
74
|
-
{ "name": "--hl--l", "value": "
|
|
70
|
+
{ "name": "--hl--xs", "value": "var(--hl-unit)", "description": "極小の行間(≒ 2px)" },
|
|
71
|
+
{ "name": "--hl--s", "value": "calc(var(--hl-unit) * 2)", "description": "小さめの行間(≒ 4px)" },
|
|
72
|
+
{ "name": "--hl--base", "value": "calc(var(--hl-unit) * 3)", "description": "基本の行間(≒ 6px)" },
|
|
73
|
+
{ "name": "--hl--l", "value": "calc(var(--hl-unit) * 4)", "description": "大きめの行間(≒ 8px)" }
|
|
75
74
|
]
|
|
76
75
|
},
|
|
77
76
|
{
|
|
78
77
|
"category": "letterSpacing",
|
|
79
78
|
"description": "文字間隔トークン。CSS変数名は --lts--{値}。",
|
|
80
79
|
"tokens": [
|
|
81
|
-
{ "name": "--lts--base", "value": "基本の文字間隔" },
|
|
82
|
-
{ "name": "--lts--s", "value": "狭めの文字間隔" },
|
|
83
|
-
{ "name": "--lts--l", "value": "広めの文字間隔" }
|
|
80
|
+
{ "name": "--lts--base", "value": "normal", "description": "基本の文字間隔" },
|
|
81
|
+
{ "name": "--lts--s", "value": "-0.05em", "description": "狭めの文字間隔" },
|
|
82
|
+
{ "name": "--lts--l", "value": "0.05em", "description": "広めの文字間隔" }
|
|
84
83
|
]
|
|
85
84
|
},
|
|
86
85
|
{
|
|
87
86
|
"category": "fontFamily",
|
|
88
87
|
"description": "フォントファミリートークン。CSS変数名は --ff--{値}。",
|
|
89
88
|
"tokens": [
|
|
90
|
-
{
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
{
|
|
90
|
+
"name": "--ff--base",
|
|
91
|
+
"value": "-apple-system, 'BlinkMacSystemFont', 'Hiragino Sans', sans-serif, 'Segoe UI Emoji'",
|
|
92
|
+
"description": "基本フォント"
|
|
93
|
+
},
|
|
94
|
+
{ "name": "--ff--accent", "value": "'Garamond', 'Baskerville', 'Times New Roman', serif", "description": "アクセントフォント" },
|
|
95
|
+
{ "name": "--ff--mono", "value": "ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace", "description": "モノスペースフォント" }
|
|
93
96
|
]
|
|
94
97
|
},
|
|
95
98
|
{
|
|
96
99
|
"category": "radius",
|
|
97
100
|
"description": "角丸トークン。CSS変数名は --bdrs--{値}。段階的に値が大きくなる。",
|
|
98
101
|
"tokens": [
|
|
99
|
-
{ "name": "--bdrs--5", "value": "角丸 step 5" },
|
|
100
|
-
{ "name": "--bdrs--10", "value": "角丸 step 10" },
|
|
101
|
-
{ "name": "--bdrs--20", "value": "角丸 step 20" },
|
|
102
|
-
{ "name": "--bdrs--30", "value": "角丸 step 30" },
|
|
103
|
-
{ "name": "--bdrs--40", "value": "角丸 step 40" },
|
|
104
|
-
{ "name": "--bdrs--50", "value": "角丸 step 50" },
|
|
105
|
-
{ "name": "--bdrs--99", "value": "完全な丸 (pill)" }
|
|
106
|
-
{ "name": "--bdrs--inner", "value": "内側の角丸 (親要素に合わせる)" }
|
|
102
|
+
{ "name": "--bdrs--5", "value": "2px", "description": "角丸 step 5" },
|
|
103
|
+
{ "name": "--bdrs--10", "value": "0.25rem", "description": "角丸 step 10(≒ 4px)" },
|
|
104
|
+
{ "name": "--bdrs--20", "value": "0.5rem", "description": "角丸 step 20(≒ 8px)" },
|
|
105
|
+
{ "name": "--bdrs--30", "value": "1rem", "description": "角丸 step 30(≒ 16px)" },
|
|
106
|
+
{ "name": "--bdrs--40", "value": "1.5rem", "description": "角丸 step 40(≒ 24px)" },
|
|
107
|
+
{ "name": "--bdrs--50", "value": "2rem", "description": "角丸 step 50(≒ 32px)" },
|
|
108
|
+
{ "name": "--bdrs--99", "value": "99rem", "description": "完全な丸 (pill)" }
|
|
107
109
|
]
|
|
108
110
|
},
|
|
109
111
|
{
|
|
110
112
|
"category": "shadow",
|
|
111
|
-
"description": "ボックスシャドウトークン。CSS変数名は --bxsh--{値}
|
|
113
|
+
"description": "ボックスシャドウトークン。CSS変数名は --bxsh--{値}。2段階の複合シャドウで構成される。--shc(シャドウカラー: hsl(220 4% 8% / 5%))を使用。",
|
|
112
114
|
"tokens": [
|
|
113
|
-
{ "name": "--bxsh--
|
|
114
|
-
{ "name": "--bxsh--
|
|
115
|
-
{ "name": "--bxsh--
|
|
116
|
-
{ "name": "--bxsh--
|
|
117
|
-
{ "name": "--bxsh--
|
|
118
|
-
{ "name": "--bxsh--50", "value": "シャドウ step 50 (最も濃い)" }
|
|
115
|
+
{ "name": "--bxsh--10", "value": "var(--sh--5), var(--sh--10)", "description": "シャドウ step 10(最も薄い)" },
|
|
116
|
+
{ "name": "--bxsh--20", "value": "var(--sh--10), var(--sh--20)", "description": "シャドウ step 20" },
|
|
117
|
+
{ "name": "--bxsh--30", "value": "var(--sh--20), var(--sh--30)", "description": "シャドウ step 30" },
|
|
118
|
+
{ "name": "--bxsh--40", "value": "var(--sh--30), var(--sh--40)", "description": "シャドウ step 40" },
|
|
119
|
+
{ "name": "--bxsh--50", "value": "var(--sh--40), var(--sh--50)", "description": "シャドウ step 50(最も濃い)" }
|
|
119
120
|
]
|
|
120
121
|
},
|
|
121
122
|
{
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { registerGetTokens } from './tools/get-tokens.js';
|
|
|
6
6
|
import { registerGetPropsSystem } from './tools/get-props-system.js';
|
|
7
7
|
import { registerGetComponent } from './tools/get-component.js';
|
|
8
8
|
import { registerSearchDocs } from './tools/search-docs.js';
|
|
9
|
+
import { registerConvertCss } from './tools/convert-css.js';
|
|
9
10
|
async function main() {
|
|
10
11
|
preloadAll();
|
|
11
12
|
const server = new McpServer({
|
|
@@ -17,6 +18,7 @@ async function main() {
|
|
|
17
18
|
registerGetPropsSystem(server);
|
|
18
19
|
registerGetComponent(server);
|
|
19
20
|
registerSearchDocs(server);
|
|
21
|
+
registerConvertCss(server);
|
|
20
22
|
const transport = new StdioServerTransport();
|
|
21
23
|
await server.connect(transport);
|
|
22
24
|
}
|
package/dist/lib/schemas.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export declare const ComponentInfoSchema: z.ZodObject<{
|
|
|
29
29
|
package: z.ZodEnum<["lism-css", "@lism-css/ui"]>;
|
|
30
30
|
category: z.ZodString;
|
|
31
31
|
description: z.ZodString;
|
|
32
|
+
aliases: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
32
33
|
props: z.ZodArray<z.ZodObject<{
|
|
33
34
|
name: z.ZodString;
|
|
34
35
|
type: z.ZodString;
|
|
@@ -58,6 +59,7 @@ export declare const ComponentInfoSchema: z.ZodObject<{
|
|
|
58
59
|
default?: string | undefined;
|
|
59
60
|
}[];
|
|
60
61
|
usage: string;
|
|
62
|
+
aliases?: string[] | undefined;
|
|
61
63
|
}, {
|
|
62
64
|
description: string;
|
|
63
65
|
category: string;
|
|
@@ -70,6 +72,7 @@ export declare const ComponentInfoSchema: z.ZodObject<{
|
|
|
70
72
|
default?: string | undefined;
|
|
71
73
|
}[];
|
|
72
74
|
usage: string;
|
|
75
|
+
aliases?: string[] | undefined;
|
|
73
76
|
}>;
|
|
74
77
|
export declare const TokenCategorySchema: z.ZodObject<{
|
|
75
78
|
category: z.ZodString;
|
package/dist/lib/schemas.js
CHANGED
package/dist/lib/search.d.ts
CHANGED
|
@@ -1,2 +1,21 @@
|
|
|
1
|
-
import type { DocsEntry, SearchResult } from './types.js';
|
|
2
|
-
|
|
1
|
+
import type { DocsEntry, PropCategory, SearchResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* コンポーネント名 → aliases (lowercase) のマップを構築。
|
|
4
|
+
* ComponentInfo に限らず { name, aliases? } を持つ任意の配列から生成できる。
|
|
5
|
+
*/
|
|
6
|
+
export declare function buildAliasMap(items: Array<{
|
|
7
|
+
name: string;
|
|
8
|
+
aliases?: string[];
|
|
9
|
+
}>): Map<string, string[]>;
|
|
10
|
+
/**
|
|
11
|
+
* CSSプロパティ名 → Lism prop名のマップを構築。
|
|
12
|
+
* 検索クエリにCSSプロパティ名が含まれていた場合に、対応するLism prop名で展開するために使用。
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildCssPropertyMap(categories: PropCategory[]): Map<string, string[]>;
|
|
15
|
+
export interface SearchDocsOptions {
|
|
16
|
+
category?: string;
|
|
17
|
+
limit?: number;
|
|
18
|
+
aliasMap?: Map<string, string[]>;
|
|
19
|
+
cssPropertyMap?: Map<string, string[]>;
|
|
20
|
+
}
|
|
21
|
+
export declare function searchDocs(entries: DocsEntry[], query: string, options?: SearchDocsOptions): SearchResult[];
|
package/dist/lib/search.js
CHANGED
|
@@ -4,7 +4,68 @@ function tokenize(text) {
|
|
|
4
4
|
.split(/[\s\-_./]+/)
|
|
5
5
|
.filter((t) => t.length > 0);
|
|
6
6
|
}
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* コンポーネント名 → aliases (lowercase) のマップを構築。
|
|
9
|
+
* ComponentInfo に限らず { name, aliases? } を持つ任意の配列から生成できる。
|
|
10
|
+
*/
|
|
11
|
+
export function buildAliasMap(items) {
|
|
12
|
+
const map = new Map();
|
|
13
|
+
for (const item of items) {
|
|
14
|
+
if (item.aliases && item.aliases.length > 0) {
|
|
15
|
+
map.set(item.name.toLowerCase(), item.aliases.map((a) => a.toLowerCase()));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return map;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* CSSプロパティ名 → Lism prop名のマップを構築。
|
|
22
|
+
* 検索クエリにCSSプロパティ名が含まれていた場合に、対応するLism prop名で展開するために使用。
|
|
23
|
+
*/
|
|
24
|
+
export function buildCssPropertyMap(categories) {
|
|
25
|
+
const map = new Map();
|
|
26
|
+
for (const cat of categories) {
|
|
27
|
+
for (const p of cat.props) {
|
|
28
|
+
// "(class: ...)" はスキップ
|
|
29
|
+
if (p.cssProperty.startsWith('(class:'))
|
|
30
|
+
continue;
|
|
31
|
+
// "--hl (CSS変数)" → "--hl"
|
|
32
|
+
const normalized = p.cssProperty.replace(/\s*\(.*\)$/, '').trim().toLowerCase();
|
|
33
|
+
if (!normalized)
|
|
34
|
+
continue;
|
|
35
|
+
const existing = map.get(normalized) ?? [];
|
|
36
|
+
existing.push(p.prop.toLowerCase());
|
|
37
|
+
map.set(normalized, existing);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return map;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 検索クエリをCSSプロパティ名で展開する。
|
|
44
|
+
* 例: "font-size" → "font-size fz", "padding" → "padding p"
|
|
45
|
+
*/
|
|
46
|
+
function expandQueryWithCssProps(query, cssPropertyMap) {
|
|
47
|
+
if (!cssPropertyMap)
|
|
48
|
+
return query;
|
|
49
|
+
const queryLower = query.toLowerCase();
|
|
50
|
+
const additions = [];
|
|
51
|
+
for (const [cssProp, lismProps] of cssPropertyMap) {
|
|
52
|
+
if (queryLower.includes(cssProp)) {
|
|
53
|
+
additions.push(...lismProps);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return additions.length > 0 ? `${query} ${additions.join(' ')}` : query;
|
|
57
|
+
}
|
|
58
|
+
// docs entry のタイトルに含まれるコンポーネント名から aliases を取得
|
|
59
|
+
function getEntryAliases(entry, aliasMap) {
|
|
60
|
+
const titleLower = entry.title.toLowerCase();
|
|
61
|
+
for (const [name, aliases] of aliasMap) {
|
|
62
|
+
if (titleLower.includes(name)) {
|
|
63
|
+
return aliases;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
function scoreEntry(entry, queryTokens, aliasMap) {
|
|
8
69
|
let score = 0;
|
|
9
70
|
const titleLower = entry.title.toLowerCase();
|
|
10
71
|
const descLower = entry.description.toLowerCase();
|
|
@@ -28,10 +89,24 @@ function scoreEntry(entry, queryTokens) {
|
|
|
28
89
|
if (snippetLower.includes(token))
|
|
29
90
|
score += 1;
|
|
30
91
|
}
|
|
92
|
+
// コンポーネントの aliases によるブースト
|
|
93
|
+
if (aliasMap) {
|
|
94
|
+
const aliases = getEntryAliases(entry, aliasMap);
|
|
95
|
+
if (aliases.length > 0) {
|
|
96
|
+
const aliasesStr = aliases.join(' ');
|
|
97
|
+
for (const token of queryTokens) {
|
|
98
|
+
if (aliasesStr.includes(token))
|
|
99
|
+
score += 5;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
31
103
|
return score;
|
|
32
104
|
}
|
|
33
|
-
export function searchDocs(entries, query,
|
|
34
|
-
const
|
|
105
|
+
export function searchDocs(entries, query, options) {
|
|
106
|
+
const { category, limit = 10, aliasMap, cssPropertyMap } = options ?? {};
|
|
107
|
+
// CSSプロパティ名をLism prop名に展開してからトークナイズ
|
|
108
|
+
const expandedQuery = expandQueryWithCssProps(query, cssPropertyMap);
|
|
109
|
+
const queryTokens = tokenize(expandedQuery);
|
|
35
110
|
if (queryTokens.length === 0)
|
|
36
111
|
return [];
|
|
37
112
|
let filtered = entries;
|
|
@@ -40,7 +115,7 @@ export function searchDocs(entries, query, category, limit = 10) {
|
|
|
40
115
|
}
|
|
41
116
|
const scored = filtered
|
|
42
117
|
.map((entry) => {
|
|
43
|
-
const score = scoreEntry(entry, queryTokens);
|
|
118
|
+
const score = scoreEntry(entry, queryTokens, aliasMap);
|
|
44
119
|
return { entry, score };
|
|
45
120
|
})
|
|
46
121
|
.filter(({ score }) => score > 0)
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { loadJSON } from '../lib/load-data.js';
|
|
3
|
+
import { PropsSystemDataSchema } from '../lib/schemas.js';
|
|
4
|
+
import { success, error, READ_ONLY_ANNOTATIONS } from '../lib/response.js';
|
|
5
|
+
// ----------------------------------------------------------------
|
|
6
|
+
// CSS パース
|
|
7
|
+
// ----------------------------------------------------------------
|
|
8
|
+
function parseCssDeclarations(cssText) {
|
|
9
|
+
// コメント除去
|
|
10
|
+
let cleaned = cssText.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
11
|
+
// セレクタ + ブレースを除去(裸の宣言リストも受け付ける)
|
|
12
|
+
cleaned = cleaned.replace(/[^{}]*\{/g, '').replace(/\}/g, '');
|
|
13
|
+
const declarations = [];
|
|
14
|
+
for (const line of cleaned.split(';')) {
|
|
15
|
+
const trimmed = line.trim();
|
|
16
|
+
if (!trimmed)
|
|
17
|
+
continue;
|
|
18
|
+
const colonIdx = trimmed.indexOf(':');
|
|
19
|
+
if (colonIdx === -1)
|
|
20
|
+
continue;
|
|
21
|
+
const property = trimmed.substring(0, colonIdx).trim().toLowerCase();
|
|
22
|
+
const value = trimmed.substring(colonIdx + 1).trim();
|
|
23
|
+
if (property && value) {
|
|
24
|
+
declarations.push({ property, value });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return declarations;
|
|
28
|
+
}
|
|
29
|
+
// ----------------------------------------------------------------
|
|
30
|
+
// CSSプロパティ → PropEntry マップ
|
|
31
|
+
// ----------------------------------------------------------------
|
|
32
|
+
function buildCssPropertyMap(categories) {
|
|
33
|
+
const map = new Map();
|
|
34
|
+
for (const cat of categories) {
|
|
35
|
+
for (const prop of cat.props) {
|
|
36
|
+
const normalized = normalizeCssPropertyName(prop.cssProperty);
|
|
37
|
+
if (normalized)
|
|
38
|
+
map.set(normalized, prop);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return map;
|
|
42
|
+
}
|
|
43
|
+
/** cssProperty フィールドの正規化 */
|
|
44
|
+
function normalizeCssPropertyName(raw) {
|
|
45
|
+
// "(class: is--container)" → スキップ(CSS プロパティではない)
|
|
46
|
+
if (raw.startsWith('(class:'))
|
|
47
|
+
return null;
|
|
48
|
+
// "--hl (CSS変数)" → "--hl"
|
|
49
|
+
return raw.replace(/\s*\(.*\)$/, '').trim().toLowerCase();
|
|
50
|
+
}
|
|
51
|
+
// ----------------------------------------------------------------
|
|
52
|
+
// 値のマッピング
|
|
53
|
+
// ----------------------------------------------------------------
|
|
54
|
+
/** よくある CSS 値 → Lism トークン値の変換テーブル */
|
|
55
|
+
const VALUE_ALIASES = {
|
|
56
|
+
column: 'col',
|
|
57
|
+
'column-reverse': 'col-r',
|
|
58
|
+
'row-reverse': 'row-r',
|
|
59
|
+
'space-between': 'between',
|
|
60
|
+
'flex-start': 'flex-s',
|
|
61
|
+
'flex-end': 'flex-e',
|
|
62
|
+
currentcolor: 'cc',
|
|
63
|
+
uppercase: 'upper',
|
|
64
|
+
lowercase: 'lower',
|
|
65
|
+
};
|
|
66
|
+
function suggestValue(propEntry, cssValue) {
|
|
67
|
+
const tokens = propEntry.values;
|
|
68
|
+
if (!tokens || tokens.length === 0)
|
|
69
|
+
return null;
|
|
70
|
+
// 直接一致
|
|
71
|
+
if (tokens.includes(cssValue))
|
|
72
|
+
return cssValue;
|
|
73
|
+
// エイリアス変換後に一致
|
|
74
|
+
const aliased = VALUE_ALIASES[cssValue.toLowerCase()];
|
|
75
|
+
if (aliased && tokens.includes(aliased))
|
|
76
|
+
return aliased;
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
// ----------------------------------------------------------------
|
|
80
|
+
// コンポーネント検出
|
|
81
|
+
// ----------------------------------------------------------------
|
|
82
|
+
function detectComponent(declarations) {
|
|
83
|
+
const propMap = new Map(declarations.map((d) => [d.property, d.value.toLowerCase()]));
|
|
84
|
+
const display = propMap.get('display');
|
|
85
|
+
const flexDirection = propMap.get('flex-direction');
|
|
86
|
+
const placeItems = propMap.get('place-items');
|
|
87
|
+
// Stack: flex + column
|
|
88
|
+
if (display === 'flex' && (flexDirection === 'column' || flexDirection === 'column-reverse')) {
|
|
89
|
+
return {
|
|
90
|
+
name: 'Stack',
|
|
91
|
+
reason: 'display: flex + flex-direction: column → Stack(縦積み Flex)',
|
|
92
|
+
implicitCss: ['display: flex', 'flex-direction: column'],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// Center: grid + place-items: center
|
|
96
|
+
if (display === 'grid' && placeItems === 'center') {
|
|
97
|
+
return {
|
|
98
|
+
name: 'Center',
|
|
99
|
+
reason: 'display: grid + place-items: center → Center(中央配置 Grid)',
|
|
100
|
+
implicitCss: ['display: grid', 'place-items: center'],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// Flex
|
|
104
|
+
if (display === 'flex') {
|
|
105
|
+
return {
|
|
106
|
+
name: 'Flex',
|
|
107
|
+
reason: 'display: flex → Flex コンポーネント',
|
|
108
|
+
implicitCss: ['display: flex'],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// Grid
|
|
112
|
+
if (display === 'grid') {
|
|
113
|
+
return {
|
|
114
|
+
name: 'Grid',
|
|
115
|
+
reason: 'display: grid → Grid コンポーネント',
|
|
116
|
+
implicitCss: ['display: grid'],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
// ----------------------------------------------------------------
|
|
122
|
+
// 変換メイン
|
|
123
|
+
// ----------------------------------------------------------------
|
|
124
|
+
function findCategory(categories, propName) {
|
|
125
|
+
for (const cat of categories) {
|
|
126
|
+
if (cat.props.some((p) => p.prop === propName))
|
|
127
|
+
return cat.category;
|
|
128
|
+
}
|
|
129
|
+
return 'unknown';
|
|
130
|
+
}
|
|
131
|
+
function buildExample(conversions, component) {
|
|
132
|
+
const tagName = component?.name ?? 'Lism';
|
|
133
|
+
const implicitCssSet = new Set(component?.implicitCss.map((c) => c.split(':')[0].trim()) ?? []);
|
|
134
|
+
const props = [];
|
|
135
|
+
const styles = [];
|
|
136
|
+
for (const conv of conversions) {
|
|
137
|
+
const cssProp = conv.css.split(':')[0].trim();
|
|
138
|
+
if (!conv.lismProp) {
|
|
139
|
+
styles.push(conv.css);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
// コンポーネントが暗黙的に付与する CSS はスキップ
|
|
143
|
+
if (implicitCssSet.has(cssProp))
|
|
144
|
+
continue;
|
|
145
|
+
if (conv.suggestedValue != null) {
|
|
146
|
+
props.push(`${conv.lismProp}='${conv.suggestedValue}'`);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
const cssValue = conv.css.split(':').slice(1).join(':').trim();
|
|
150
|
+
props.push(`${conv.lismProp}='${cssValue}'`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
let result = `<${tagName}`;
|
|
154
|
+
if (props.length > 0)
|
|
155
|
+
result += ` ${props.join(' ')}`;
|
|
156
|
+
if (styles.length > 0) {
|
|
157
|
+
result += ` style="${styles.map((s) => s.replace(/"/g, "'")).join('; ')}"`;
|
|
158
|
+
}
|
|
159
|
+
result += `>...</${tagName}>`;
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
// ----------------------------------------------------------------
|
|
163
|
+
// ツール登録
|
|
164
|
+
// ----------------------------------------------------------------
|
|
165
|
+
export function registerConvertCss(server) {
|
|
166
|
+
server.registerTool('convert_css', {
|
|
167
|
+
description: 'Convert CSS code to lism-css props, utility classes, and component suggestions. Accepts CSS declarations (with or without selectors) and returns the equivalent lism-css representation. Use this to migrate existing CSS to lism-css, or to understand how CSS properties map to lism-css.',
|
|
168
|
+
inputSchema: {
|
|
169
|
+
css: z
|
|
170
|
+
.string()
|
|
171
|
+
.describe('CSS code to convert. Accepts a full rule block with selector (e.g. ".foo { padding: 1rem; }") or bare declarations (e.g. "padding: 1rem; font-size: 16px;").'),
|
|
172
|
+
},
|
|
173
|
+
annotations: READ_ONLY_ANNOTATIONS,
|
|
174
|
+
}, ({ css }) => {
|
|
175
|
+
try {
|
|
176
|
+
const propsData = loadJSON('props-system.json', PropsSystemDataSchema);
|
|
177
|
+
const cssPropertyMap = buildCssPropertyMap(propsData.categories);
|
|
178
|
+
const declarations = parseCssDeclarations(css);
|
|
179
|
+
if (declarations.length === 0) {
|
|
180
|
+
return error('CSS 宣言が見つかりません。"property: value;" 形式の CSS を入力してください。');
|
|
181
|
+
}
|
|
182
|
+
// 各宣言を変換
|
|
183
|
+
const conversions = declarations.map((decl) => {
|
|
184
|
+
const propEntry = cssPropertyMap.get(decl.property);
|
|
185
|
+
if (!propEntry) {
|
|
186
|
+
return {
|
|
187
|
+
css: `${decl.property}: ${decl.value}`,
|
|
188
|
+
lismProp: null,
|
|
189
|
+
suggestedValue: null,
|
|
190
|
+
availableTokens: null,
|
|
191
|
+
note: 'Lism Props に該当なし。style で直接指定してください。',
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const suggested = suggestValue(propEntry, decl.value);
|
|
195
|
+
const category = findCategory(propsData.categories, propEntry.prop);
|
|
196
|
+
return {
|
|
197
|
+
css: `${decl.property}: ${decl.value}`,
|
|
198
|
+
lismProp: propEntry.prop,
|
|
199
|
+
suggestedValue: suggested,
|
|
200
|
+
availableTokens: propEntry.values ?? null,
|
|
201
|
+
note: suggested
|
|
202
|
+
? `トークン値 '${suggested}' を使用(カテゴリ: ${category})`
|
|
203
|
+
: propEntry.values && propEntry.values.length > 0
|
|
204
|
+
? `カスタム値。利用可能なトークン: ${propEntry.values.join(', ')}(カテゴリ: ${category})`
|
|
205
|
+
: `カスタム値として指定(カテゴリ: ${category})`,
|
|
206
|
+
};
|
|
207
|
+
});
|
|
208
|
+
// コンポーネント検出
|
|
209
|
+
const suggestedComponent = detectComponent(declarations);
|
|
210
|
+
// 使用例
|
|
211
|
+
const example = buildExample(conversions, suggestedComponent);
|
|
212
|
+
return success({
|
|
213
|
+
conversions,
|
|
214
|
+
suggestedComponent,
|
|
215
|
+
example,
|
|
216
|
+
tip: 'トークン値にマッチしない値は style 属性で CSS 変数として指定できます(例: style="--p: 1rem")。get_props_system で各 prop の詳細を確認できます。',
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
catch (e) {
|
|
220
|
+
return error(`CSS 変換に失敗しました: ${e instanceof Error ? e.message : String(e)}`);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|