@mindfiredigital/ignix-lite-mcp 1.2.0 → 1.4.0
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/README.md +108 -0
- package/dist/server.js +187 -1954
- package/dist/server.js.map +1 -1
- package/dist/utils/check-api.js +2 -0
- package/dist/utils/check-api.js.map +1 -1
- package/package.json +14 -6
- package/.turbo/turbo-build.log +0 -25
- package/CHANGELOG.md +0 -7
- package/dist/manifests/accordion.json +0 -61
- package/dist/manifests/alert.json +0 -69
- package/dist/manifests/avatar.json +0 -75
- package/dist/manifests/badge.json +0 -74
- package/dist/manifests/breadcrumb.json +0 -87
- package/dist/manifests/button.json +0 -85
- package/dist/manifests/card.json +0 -91
- package/dist/manifests/checkbox.json +0 -122
- package/dist/manifests/codeblock.json +0 -63
- package/dist/manifests/combobox.json +0 -33
- package/dist/manifests/dialog.json +0 -64
- package/dist/manifests/divider.json +0 -47
- package/dist/manifests/dropdown.json +0 -105
- package/dist/manifests/form.json +0 -81
- package/dist/manifests/grid.json +0 -143
- package/dist/manifests/input.json +0 -99
- package/dist/manifests/meter.json +0 -103
- package/dist/manifests/navigation.json +0 -70
- package/dist/manifests/progress.json +0 -88
- package/dist/manifests/radio.json +0 -121
- package/dist/manifests/select.json +0 -109
- package/dist/manifests/skeleton.json +0 -101
- package/dist/manifests/tab.json +0 -88
- package/dist/manifests/table.json +0 -92
- package/dist/manifests/textarea.json +0 -117
- package/dist/manifests/toast.json +0 -157
- package/dist/manifests/tooltip.json +0 -115
- package/dist/vector-index.json +0 -14015
- package/src/context/api-context.ts +0 -14
- package/src/global.d.ts +0 -15
- package/src/manifests/accordion.json +0 -61
- package/src/manifests/alert.json +0 -69
- package/src/manifests/avatar.json +0 -75
- package/src/manifests/badge.json +0 -74
- package/src/manifests/breadcrumb.json +0 -87
- package/src/manifests/button.json +0 -85
- package/src/manifests/card.json +0 -91
- package/src/manifests/checkbox.json +0 -122
- package/src/manifests/codeblock.json +0 -63
- package/src/manifests/combobox.json +0 -33
- package/src/manifests/dialog.json +0 -64
- package/src/manifests/divider.json +0 -47
- package/src/manifests/dropdown.json +0 -105
- package/src/manifests/form.json +0 -81
- package/src/manifests/grid.json +0 -143
- package/src/manifests/index.ts +0 -45
- package/src/manifests/input.json +0 -99
- package/src/manifests/meter.json +0 -103
- package/src/manifests/navigation.json +0 -70
- package/src/manifests/progress.json +0 -88
- package/src/manifests/radio.json +0 -121
- package/src/manifests/select.json +0 -109
- package/src/manifests/skeleton.json +0 -101
- package/src/manifests/tab.json +0 -88
- package/src/manifests/table.json +0 -92
- package/src/manifests/textarea.json +0 -117
- package/src/manifests/toast.json +0 -157
- package/src/manifests/tooltip.json +0 -115
- package/src/server.ts +0 -201
- package/src/tools/build-index.ts +0 -55
- package/src/tools/check-a11y.ts +0 -106
- package/src/tools/embedder.ts +0 -18
- package/src/tools/generate-theme.ts +0 -42
- package/src/tools/get-emmet.ts +0 -64
- package/src/tools/get-manifests.ts +0 -55
- package/src/tools/intent-engine.ts +0 -197
- package/src/tools/list-components.ts +0 -20
- package/src/tools/search-index.ts +0 -66
- package/src/tools/theme-palette.ts +0 -65
- package/src/tools/theme-tokens.ts +0 -176
- package/src/tools/validator.ts +0 -367
- package/src/types.ts +0 -63
- package/src/utils/a11y-rules.ts +0 -873
- package/src/utils/a11y-types.ts +0 -15
- package/src/utils/check-api.ts +0 -13
- package/src/utils/cosine.ts +0 -15
- package/src/utils/emmet-helpers.ts +0 -171
- package/src/utils/intent-helpers.ts +0 -66
- package/src/utils/intent-parser.ts +0 -186
- package/src/utils/tokenizer.ts +0 -7
- package/tsconfig.json +0 -14
- package/tsup.config.ts +0 -13
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { searchIndex } from './search-index.js'
|
|
2
|
-
import { getIntentEntries, scoreEntry } from '../utils/intent-parser.js'
|
|
3
|
-
import { getTokenCount } from '../utils/tokenizer.js'
|
|
4
|
-
import { tokenise } from '../utils/intent-helpers.js'
|
|
5
|
-
import {
|
|
6
|
-
expandEmmet,
|
|
7
|
-
extractComponents,
|
|
8
|
-
interpolateEmmet
|
|
9
|
-
} from '../utils/emmet-helpers.js'
|
|
10
|
-
|
|
11
|
-
import type { IntentEntry } from '../utils/intent-parser.js'
|
|
12
|
-
|
|
13
|
-
// ─── Layer 1: api-full.txt INTENTS lookup ───────────────────────────────────
|
|
14
|
-
|
|
15
|
-
type IntentMatch = {
|
|
16
|
-
name: string
|
|
17
|
-
emmet: string
|
|
18
|
-
score: number
|
|
19
|
-
source: 'intent-table' | 'vector-index'
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const LAYER1_THRESHOLD = 8 // minimum score to trust an intent-table hit
|
|
23
|
-
|
|
24
|
-
function searchIntentTable(description: string): IntentMatch | null {
|
|
25
|
-
const entries = getIntentEntries()
|
|
26
|
-
if (entries.length === 0) return null
|
|
27
|
-
|
|
28
|
-
const queryWords = tokenise(description)
|
|
29
|
-
|
|
30
|
-
// Find all matches meeting the threshold
|
|
31
|
-
const candidates: {
|
|
32
|
-
entry: IntentEntry
|
|
33
|
-
score: number
|
|
34
|
-
density: number
|
|
35
|
-
components: string[]
|
|
36
|
-
}[] = []
|
|
37
|
-
let bestSingle: (IntentMatch & { density: number }) | null = null
|
|
38
|
-
|
|
39
|
-
for (const entry of entries) {
|
|
40
|
-
const { score, density } = scoreEntry(entry, queryWords)
|
|
41
|
-
if (score >= LAYER1_THRESHOLD) {
|
|
42
|
-
const components = extractComponents(entry.emmet)
|
|
43
|
-
candidates.push({ entry, score, density, components })
|
|
44
|
-
|
|
45
|
-
const isBetter =
|
|
46
|
-
!bestSingle ||
|
|
47
|
-
score > bestSingle.score ||
|
|
48
|
-
(score === bestSingle.score && density > bestSingle.density)
|
|
49
|
-
if (isBetter) {
|
|
50
|
-
bestSingle = {
|
|
51
|
-
name: entry.name,
|
|
52
|
-
emmet: entry.emmet,
|
|
53
|
-
score,
|
|
54
|
-
density,
|
|
55
|
-
source: 'intent-table'
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// If a single template matches with exceptionally high score, trust it directly
|
|
62
|
-
if (bestSingle && bestSingle.score >= 18) {
|
|
63
|
-
return bestSingle
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Check if the user explicitly requested stitching using conjunction words (and, plus, +, comma)
|
|
67
|
-
const isStitchRequested =
|
|
68
|
-
/\b(and|plus|\+)\b/i.test(description) || description.includes(',')
|
|
69
|
-
|
|
70
|
-
// Otherwise, check if we can stitch multiple distinct components together (synthesis)
|
|
71
|
-
if (isStitchRequested && candidates.length >= 2) {
|
|
72
|
-
// Sort candidates by score (descending)
|
|
73
|
-
candidates.sort((a, b) => b.score - a.score || b.density - a.density)
|
|
74
|
-
|
|
75
|
-
const selected: typeof candidates = []
|
|
76
|
-
const usedComponents = new Set<string>()
|
|
77
|
-
|
|
78
|
-
for (const c of candidates) {
|
|
79
|
-
// Check if this candidate is disjoint from the already selected components
|
|
80
|
-
const hasOverlap = c.components.some((comp) => usedComponents.has(comp))
|
|
81
|
-
if (!hasOverlap) {
|
|
82
|
-
selected.push(c)
|
|
83
|
-
c.components.forEach((comp) => usedComponents.add(comp))
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// If we gathered at least 2 distinct templates, stitch them!
|
|
88
|
-
if (selected.length >= 2) {
|
|
89
|
-
// Re-sort selected in the order they appear in the user description to look natural
|
|
90
|
-
selected.sort((a, b) => {
|
|
91
|
-
const indexA = description
|
|
92
|
-
.toLowerCase()
|
|
93
|
-
.indexOf(tokenise(a.entry.name)[0])
|
|
94
|
-
const indexB = description
|
|
95
|
-
.toLowerCase()
|
|
96
|
-
.indexOf(tokenise(b.entry.name)[0])
|
|
97
|
-
return indexA - indexB
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
const combinedEmmet = selected.map((s) => s.entry.emmet).join('+')
|
|
101
|
-
const totalScore = selected.reduce((sum, s) => sum + s.score, 0)
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
name: selected.map((s) => s.entry.name).join(' and '),
|
|
105
|
-
emmet: combinedEmmet,
|
|
106
|
-
score: totalScore,
|
|
107
|
-
source: 'intent-table'
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return bestSingle
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ─── Layer 2: vector-index fallback ─────────────────────────────────────────
|
|
116
|
-
|
|
117
|
-
function searchVectorLayer(description: string): IntentMatch | null {
|
|
118
|
-
const results = searchIndex(description)
|
|
119
|
-
if (results.length === 0) return null
|
|
120
|
-
const top = results[0]
|
|
121
|
-
return {
|
|
122
|
-
name: top.name,
|
|
123
|
-
emmet: top.emmet,
|
|
124
|
-
score: top.score,
|
|
125
|
-
source: 'vector-index'
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// ─── Public: howToBuild ──────────────────────────────────────────────────────
|
|
130
|
-
|
|
131
|
-
export async function howToBuild(description: string) {
|
|
132
|
-
let cleanDesc = description.trim()
|
|
133
|
-
if (cleanDesc.startsWith('{') && cleanDesc.endsWith('}')) {
|
|
134
|
-
try {
|
|
135
|
-
const parsed = JSON.parse(cleanDesc)
|
|
136
|
-
if (parsed && typeof parsed.description === 'string') {
|
|
137
|
-
cleanDesc = parsed.description
|
|
138
|
-
} else if (parsed && typeof parsed.text === 'string') {
|
|
139
|
-
cleanDesc = parsed.text
|
|
140
|
-
}
|
|
141
|
-
} catch {
|
|
142
|
-
// Ignore parsing errors, keep original string
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Layer 1 — fast, deterministic, hand-crafted intent table
|
|
147
|
-
let match = searchIntentTable(cleanDesc)
|
|
148
|
-
|
|
149
|
-
// Layer 2 — vector index fallback for novel / unknown queries
|
|
150
|
-
if (!match) {
|
|
151
|
-
match = searchVectorLayer(cleanDesc)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (!match) {
|
|
155
|
-
return {
|
|
156
|
-
content: [
|
|
157
|
-
{
|
|
158
|
-
type: 'text',
|
|
159
|
-
text: JSON.stringify({
|
|
160
|
-
emmet: '',
|
|
161
|
-
html: '',
|
|
162
|
-
components_used: [],
|
|
163
|
-
confidence: 0,
|
|
164
|
-
tokens: 0,
|
|
165
|
-
tokens_used: 15,
|
|
166
|
-
source: 'no-match',
|
|
167
|
-
suggestion:
|
|
168
|
-
'Call list_components() to see all available components, or try a different description.'
|
|
169
|
-
})
|
|
170
|
-
}
|
|
171
|
-
]
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Apply dynamic interpolation to customize labels, types, and colors on the fly
|
|
176
|
-
const emmet = interpolateEmmet(match.emmet, cleanDesc)
|
|
177
|
-
const components_used = extractComponents(emmet)
|
|
178
|
-
|
|
179
|
-
const confidence = Math.min(1, match.score / 40)
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
content: [
|
|
183
|
-
{
|
|
184
|
-
type: 'text',
|
|
185
|
-
text: JSON.stringify({
|
|
186
|
-
emmet,
|
|
187
|
-
html: expandEmmet(emmet),
|
|
188
|
-
components_used,
|
|
189
|
-
confidence: Number(confidence.toFixed(2)),
|
|
190
|
-
tokens: getTokenCount(emmet),
|
|
191
|
-
tokens_used: 15,
|
|
192
|
-
source: match.source // tells caller which layer matched
|
|
193
|
-
})
|
|
194
|
-
}
|
|
195
|
-
]
|
|
196
|
-
}
|
|
197
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { manifests } from '../manifests/index.js'
|
|
2
|
-
|
|
3
|
-
import type { MCPResponse } from '../types.js'
|
|
4
|
-
|
|
5
|
-
export function listComponents(): MCPResponse {
|
|
6
|
-
const components = Object.keys(manifests).sort()
|
|
7
|
-
|
|
8
|
-
return {
|
|
9
|
-
content: [
|
|
10
|
-
{
|
|
11
|
-
type: 'text',
|
|
12
|
-
text: JSON.stringify({
|
|
13
|
-
components,
|
|
14
|
-
count: components.length,
|
|
15
|
-
tokens_used: components.length
|
|
16
|
-
})
|
|
17
|
-
}
|
|
18
|
-
]
|
|
19
|
-
}
|
|
20
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import path from 'path'
|
|
2
|
-
import { readFileSync, existsSync } from 'fs'
|
|
3
|
-
import { fileURLToPath } from 'url'
|
|
4
|
-
import { embedText } from './embedder.js'
|
|
5
|
-
import { cosineSimilarity } from '../utils/cosine.js'
|
|
6
|
-
|
|
7
|
-
type IndexItem = {
|
|
8
|
-
name: string
|
|
9
|
-
emmet: string
|
|
10
|
-
searchable: string
|
|
11
|
-
embedding: number[]
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
15
|
-
const __dirname = path.dirname(__filename)
|
|
16
|
-
|
|
17
|
-
let _index: IndexItem[] | null = null
|
|
18
|
-
|
|
19
|
-
function loadIndex(): IndexItem[] {
|
|
20
|
-
if (_index) return _index
|
|
21
|
-
const indexPath = path.resolve(__dirname, '../../dist/vector-index.json')
|
|
22
|
-
if (!existsSync(indexPath)) {
|
|
23
|
-
console.warn(
|
|
24
|
-
`[search-index] dist/vector-index.json not found at: ${indexPath} - run pnpm build:index`
|
|
25
|
-
)
|
|
26
|
-
_index = []
|
|
27
|
-
return _index
|
|
28
|
-
}
|
|
29
|
-
try {
|
|
30
|
-
_index = JSON.parse(readFileSync(indexPath, 'utf8'))
|
|
31
|
-
} catch (err) {
|
|
32
|
-
console.error(`[search-index] Failed to load index from ${indexPath}:`, err)
|
|
33
|
-
_index = []
|
|
34
|
-
}
|
|
35
|
-
return _index!
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function searchIndex(description: string) {
|
|
39
|
-
const index = loadIndex()
|
|
40
|
-
const queryEmbedding = embedText(description)
|
|
41
|
-
|
|
42
|
-
const words = description.toLowerCase().split(/\s+/)
|
|
43
|
-
const ranked = index
|
|
44
|
-
.map((item) => {
|
|
45
|
-
const similarity = cosineSimilarity(queryEmbedding, item.embedding)
|
|
46
|
-
let boost = 0
|
|
47
|
-
const searchable = item.searchable.toLowerCase()
|
|
48
|
-
|
|
49
|
-
words.forEach((word) => {
|
|
50
|
-
if (item.name.toLowerCase() === word) {
|
|
51
|
-
boost += 2
|
|
52
|
-
} else if (item.name.toLowerCase().includes(word)) {
|
|
53
|
-
boost += 1
|
|
54
|
-
}
|
|
55
|
-
if (searchable.includes(word)) {
|
|
56
|
-
boost += 0.3
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
return {
|
|
60
|
-
...item,
|
|
61
|
-
score: similarity + boost
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
.sort((a, b) => b.score - a.score)
|
|
65
|
-
return ranked.slice(0, 3)
|
|
66
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
export interface ColorEntry {
|
|
2
|
-
primary: string
|
|
3
|
-
contrast: string
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export const PALETTE: Record<string, ColorEntry> = {
|
|
7
|
-
red: { primary: '#ef4444', contrast: '#ffffff' },
|
|
8
|
-
orange: { primary: '#f97316', contrast: '#ffffff' },
|
|
9
|
-
amber: { primary: '#f59e0b', contrast: '#111827' },
|
|
10
|
-
yellow: { primary: '#eab308', contrast: '#111827' },
|
|
11
|
-
lime: { primary: '#84cc16', contrast: '#111827' },
|
|
12
|
-
green: { primary: '#22c55e', contrast: '#ffffff' },
|
|
13
|
-
emerald: { primary: '#10b981', contrast: '#ffffff' },
|
|
14
|
-
teal: { primary: '#14b8a6', contrast: '#ffffff' },
|
|
15
|
-
cyan: { primary: '#06b6d4', contrast: '#111827' },
|
|
16
|
-
sky: { primary: '#0ea5e9', contrast: '#111827' },
|
|
17
|
-
blue: { primary: '#3b82f6', contrast: '#ffffff' },
|
|
18
|
-
indigo: { primary: '#6366f1', contrast: '#ffffff' },
|
|
19
|
-
violet: { primary: '#8b5cf6', contrast: '#ffffff' },
|
|
20
|
-
purple: { primary: '#a855f7', contrast: '#ffffff' },
|
|
21
|
-
fuchsia: { primary: '#d946ef', contrast: '#ffffff' },
|
|
22
|
-
pink: { primary: '#ec4899', contrast: '#ffffff' },
|
|
23
|
-
rose: { primary: '#f43f5e', contrast: '#ffffff' },
|
|
24
|
-
cyberpunk: { primary: '#ec4899', contrast: '#ffffff' },
|
|
25
|
-
neon: { primary: '#d946ef', contrast: '#ffffff' },
|
|
26
|
-
eco: { primary: '#10b981', contrast: '#ffffff' },
|
|
27
|
-
forest: { primary: '#0f766e', contrast: '#ffffff' },
|
|
28
|
-
coffee: { primary: '#78350f', contrast: '#ffffff' },
|
|
29
|
-
ocean: { primary: '#0284c7', contrast: '#ffffff' },
|
|
30
|
-
sunset: { primary: '#f97316', contrast: '#ffffff' },
|
|
31
|
-
midnight: { primary: '#3730a3', contrast: '#ffffff' },
|
|
32
|
-
lavender: { primary: '#8b5cf6', contrast: '#ffffff' },
|
|
33
|
-
coral: { primary: '#f43f5e', contrast: '#ffffff' },
|
|
34
|
-
slate: { primary: '#64748b', contrast: '#ffffff' },
|
|
35
|
-
gold: { primary: '#d97706', contrast: '#111827' },
|
|
36
|
-
silver: { primary: '#94a3b8', contrast: '#111827' }
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function expandHex(hex: string): string {
|
|
40
|
-
const h = hex.replace('#', '')
|
|
41
|
-
return h.length === 3 ? '#' + h[0] + h[0] + h[1] + h[1] + h[2] + h[2] : hex
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function contrastFor(hex6: string): string {
|
|
45
|
-
const h = hex6.replace('#', '')
|
|
46
|
-
const r = parseInt(h.substring(0, 2), 16)
|
|
47
|
-
const g = parseInt(h.substring(2, 4), 16)
|
|
48
|
-
const b = parseInt(h.substring(4, 6), 16)
|
|
49
|
-
return (r * 299 + g * 587 + b * 114) / 1000 > 165 ? '#111827' : '#ffffff'
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function resolveColor(query: string): ColorEntry {
|
|
53
|
-
const hexMatch = query.match(/#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})\b/)
|
|
54
|
-
if (hexMatch) {
|
|
55
|
-
const primary = expandHex(hexMatch[0])
|
|
56
|
-
return { primary, contrast: contrastFor(primary) }
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const sorted = Object.keys(PALETTE).sort((a, b) => b.length - a.length)
|
|
60
|
-
for (const name of sorted) {
|
|
61
|
-
if (query.includes(name)) return PALETTE[name]
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return { primary: '#6366f1', contrast: '#ffffff' }
|
|
65
|
-
}
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import { resolveColor } from './theme-palette.js'
|
|
2
|
-
|
|
3
|
-
export interface ThemeTokens {
|
|
4
|
-
primary: string
|
|
5
|
-
primaryHover: string
|
|
6
|
-
primaryContrast: string
|
|
7
|
-
primaryBg: string
|
|
8
|
-
|
|
9
|
-
danger: string
|
|
10
|
-
dangerText: string
|
|
11
|
-
dangerBg: string
|
|
12
|
-
|
|
13
|
-
warning: string
|
|
14
|
-
warningBg: string
|
|
15
|
-
|
|
16
|
-
success: string
|
|
17
|
-
successBg: string
|
|
18
|
-
|
|
19
|
-
infoBg: string
|
|
20
|
-
neutral: string
|
|
21
|
-
divider: string
|
|
22
|
-
|
|
23
|
-
surface: string
|
|
24
|
-
surfaceRaised: string
|
|
25
|
-
|
|
26
|
-
text: string
|
|
27
|
-
textMuted: string
|
|
28
|
-
border: string
|
|
29
|
-
|
|
30
|
-
radius: string
|
|
31
|
-
radiusLg: string
|
|
32
|
-
badgeRadius: string
|
|
33
|
-
|
|
34
|
-
skeletonBg: string
|
|
35
|
-
skeletonShimmer: string
|
|
36
|
-
|
|
37
|
-
gradientStart: string
|
|
38
|
-
gradientEnd: string
|
|
39
|
-
gradientBgEnd: string
|
|
40
|
-
|
|
41
|
-
isDark: boolean
|
|
42
|
-
resolvedPrimary: string
|
|
43
|
-
resolvedContrast: string
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function resolveTokens(query: string): ThemeTokens {
|
|
47
|
-
const { primary, contrast } = resolveColor(query)
|
|
48
|
-
|
|
49
|
-
const isDark =
|
|
50
|
-
query.includes('dark') ||
|
|
51
|
-
query.includes('night') ||
|
|
52
|
-
query.includes('midnight') ||
|
|
53
|
-
query.includes('cyberpunk') ||
|
|
54
|
-
query.includes('dim')
|
|
55
|
-
|
|
56
|
-
const sharp =
|
|
57
|
-
query.includes('sharp') ||
|
|
58
|
-
query.includes('flat') ||
|
|
59
|
-
query.includes('square')
|
|
60
|
-
const round =
|
|
61
|
-
query.includes('round') || query.includes('pill') || query.includes('soft')
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
resolvedPrimary: primary,
|
|
65
|
-
resolvedContrast: contrast,
|
|
66
|
-
isDark,
|
|
67
|
-
|
|
68
|
-
primary,
|
|
69
|
-
primaryHover: isDark
|
|
70
|
-
? 'color-mix(in srgb, var(--ix-primary) 75%, white)'
|
|
71
|
-
: 'color-mix(in srgb, var(--ix-primary) 85%, black)',
|
|
72
|
-
primaryContrast: isDark
|
|
73
|
-
? 'color-mix(in srgb, var(--ix-primary) 40%, white)'
|
|
74
|
-
: contrast,
|
|
75
|
-
primaryBg: isDark
|
|
76
|
-
? 'color-mix(in srgb, var(--ix-primary) 20%, #0f172a)'
|
|
77
|
-
: 'color-mix(in srgb, var(--ix-primary) 15%, white)',
|
|
78
|
-
|
|
79
|
-
danger: '#ef4444',
|
|
80
|
-
dangerText: isDark ? '#f87171' : '#ef4444',
|
|
81
|
-
dangerBg: isDark ? '#3f1d1d' : '#fee2e2',
|
|
82
|
-
|
|
83
|
-
warning: isDark ? '#fbbf24' : '#f59e0b',
|
|
84
|
-
warningBg: isDark ? '#3f2e05' : '#fef3c7',
|
|
85
|
-
|
|
86
|
-
success: '#22c55e',
|
|
87
|
-
successBg: isDark ? '#052e16' : '#dcfce7',
|
|
88
|
-
|
|
89
|
-
infoBg: isDark ? '#1e293b' : '#f3f4f6',
|
|
90
|
-
neutral: isDark ? '#9ca3af' : '#6b7280',
|
|
91
|
-
divider: isDark ? '#475569' : '#9ca3af',
|
|
92
|
-
|
|
93
|
-
surface: isDark ? '#0f172a' : '#f9fafb',
|
|
94
|
-
surfaceRaised: isDark ? '#1e293b' : '#ffffff',
|
|
95
|
-
|
|
96
|
-
text: isDark ? '#f9fafb' : '#111827',
|
|
97
|
-
textMuted: isDark ? '#94a3b8' : '#6b7280',
|
|
98
|
-
border: isDark ? '#334155' : '#e5e7eb',
|
|
99
|
-
|
|
100
|
-
radius: sharp ? '0px' : round ? '0.75rem' : '0.375rem',
|
|
101
|
-
radiusLg: sharp ? '0px' : round ? '1rem' : '0.5rem',
|
|
102
|
-
badgeRadius: '999px',
|
|
103
|
-
|
|
104
|
-
skeletonBg: isDark ? '#1e293b' : '#e5e7eb',
|
|
105
|
-
skeletonShimmer: isDark ? 'rgba(255,255,255,0.1)' : 'rgba(255,255,255,0.6)',
|
|
106
|
-
|
|
107
|
-
gradientStart: primary,
|
|
108
|
-
gradientEnd: '#fffbd5',
|
|
109
|
-
gradientBgEnd: isDark ? '#aba67a' : '#d7d4bc'
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export function buildCss(t: ThemeTokens): string {
|
|
114
|
-
return `:root {
|
|
115
|
-
--ix-primary: ${t.primary};
|
|
116
|
-
--ix-primary-hover: ${t.primaryHover};
|
|
117
|
-
--ix-primary-contrast: ${t.primaryContrast};
|
|
118
|
-
|
|
119
|
-
--ix-danger: ${t.danger};
|
|
120
|
-
--ix-danger-text: ${t.dangerText};
|
|
121
|
-
|
|
122
|
-
--ix-warning: ${t.warning};
|
|
123
|
-
--ix-success: ${t.success};
|
|
124
|
-
--ix-neutral: ${t.neutral};
|
|
125
|
-
--ix-ghost: transparent;
|
|
126
|
-
|
|
127
|
-
--ix-primary-bg: ${t.primaryBg};
|
|
128
|
-
--ix-danger-bg: ${t.dangerBg};
|
|
129
|
-
--ix-warning-bg: ${t.warningBg};
|
|
130
|
-
--ix-success-bg: ${t.successBg};
|
|
131
|
-
--ix-info-bg: ${t.infoBg};
|
|
132
|
-
|
|
133
|
-
--ix-on-danger: #ffffff;
|
|
134
|
-
--ix-on-warning: #000000;
|
|
135
|
-
--ix-on-success: #ffffff;
|
|
136
|
-
--ix-on-primary: ${t.resolvedContrast};
|
|
137
|
-
|
|
138
|
-
--ix-divider: ${t.divider};
|
|
139
|
-
|
|
140
|
-
--ix-surface: ${t.surface};
|
|
141
|
-
--ix-surface-raised: ${t.surfaceRaised};
|
|
142
|
-
|
|
143
|
-
--ix-text: ${t.text};
|
|
144
|
-
--ix-text-muted: ${t.textMuted};
|
|
145
|
-
|
|
146
|
-
--ix-border: ${t.border};
|
|
147
|
-
--ix-focus: 2px solid var(--ix-primary);
|
|
148
|
-
|
|
149
|
-
--ix-font: 'Segoe UI Variable', 'Segoe UI', 'Roboto', 'Noto Sans', system-ui, sans-serif;
|
|
150
|
-
--ix-font-mono: 'JetBrains Mono', 'SFMono-Regular', Consolas, monospace;
|
|
151
|
-
|
|
152
|
-
--ix-size-xs: 0.75rem;
|
|
153
|
-
--ix-size-sm: 0.875rem;
|
|
154
|
-
--ix-size-md: 1rem;
|
|
155
|
-
--ix-size-lg: 1.125rem;
|
|
156
|
-
--ix-size-xl: 1.25rem;
|
|
157
|
-
|
|
158
|
-
--ix-line-height: 1.5;
|
|
159
|
-
|
|
160
|
-
--ix-space-sm: 0.5rem;
|
|
161
|
-
--ix-space-md: 1rem;
|
|
162
|
-
--ix-space-lg: 1.5rem;
|
|
163
|
-
|
|
164
|
-
--ix-radius: ${t.radius};
|
|
165
|
-
--ix-radius-lg: ${t.radiusLg};
|
|
166
|
-
--ix-badge-radius: ${t.badgeRadius};
|
|
167
|
-
|
|
168
|
-
--ix-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
169
|
-
|
|
170
|
-
--ix-gradient: linear-gradient(to right, ${t.gradientStart}, ${t.gradientEnd});
|
|
171
|
-
--ix-gradient-bg: linear-gradient(to right, ${t.gradientStart}, ${t.gradientBgEnd});
|
|
172
|
-
|
|
173
|
-
--ix-skeleton-bg: ${t.skeletonBg};
|
|
174
|
-
--ix-skeleton-shimmer: ${t.skeletonShimmer};
|
|
175
|
-
}`
|
|
176
|
-
}
|