@pandacss/language-server 0.0.0-dev-20230607205019
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/LICENSE.md +21 -0
- package/Roboto-Medium.ttf +0 -0
- package/dist/index.js +46760 -0
- package/index.ts +1 -0
- package/package.json +49 -0
- package/src/builder-resolver.ts +82 -0
- package/src/capabilities.ts +25 -0
- package/src/features/color-hints.ts +68 -0
- package/src/features/completion.ts +34 -0
- package/src/features/diagnostics.ts +93 -0
- package/src/features/hover.ts +79 -0
- package/src/features/index.ts +5 -0
- package/src/features/inlay-hints.ts +61 -0
- package/src/index.ts +130 -0
- package/src/setup-builder.ts +252 -0
- package/src/tokens/color2k-to-vscode-color.ts +18 -0
- package/src/tokens/error.ts +3 -0
- package/src/tokens/expand-token-fn.ts +43 -0
- package/src/tokens/generate-keyframe-css.ts +28 -0
- package/src/tokens/is-color.ts +216 -0
- package/src/tokens/render-font-size-preview.ts +49 -0
- package/src/tokens/render-token-color-preview.ts +63 -0
- package/src/tokens/setup-tokens-helpers.ts +601 -0
- package/src/tokens/utils.ts +82 -0
- package/src/uri-to-path.ts +38 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { Builder } from '@pandacss/node'
|
|
2
|
+
import { type Connection, DidChangeConfigurationNotification, TextDocuments } from 'vscode-languageserver'
|
|
3
|
+
import { TextDocument } from 'vscode-languageserver-textdocument'
|
|
4
|
+
import { serverCapabilities } from './capabilities'
|
|
5
|
+
import { type PandaVSCodeSettings, defaultSettings, getFlattenedSettings } from '@pandacss/extension-shared'
|
|
6
|
+
import glob from 'fast-glob'
|
|
7
|
+
import { uriToPath } from './uri-to-path'
|
|
8
|
+
import { BuilderResolver } from './builder-resolver'
|
|
9
|
+
|
|
10
|
+
const builderResolver = new BuilderResolver()
|
|
11
|
+
|
|
12
|
+
const ref = {
|
|
13
|
+
settings: null as PandaVSCodeSettings | null,
|
|
14
|
+
/**
|
|
15
|
+
* current builder's context, used by most features as we can only be in one context at a time
|
|
16
|
+
* depending on the active document
|
|
17
|
+
*/
|
|
18
|
+
context: null as unknown as Builder['context'],
|
|
19
|
+
synchronizing: false as Promise<void> | false,
|
|
20
|
+
//
|
|
21
|
+
activeDocumentFilepath: '',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let hasConfigurationCapability = false
|
|
25
|
+
let hasWorkspaceFolderCapability = false
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Setup builder
|
|
29
|
+
* - panda.config detection & context loading
|
|
30
|
+
* - reload on panda.config change
|
|
31
|
+
* - make the builder.setup promise shared so it can be awaited by multiple features
|
|
32
|
+
*/
|
|
33
|
+
export function setupBuilder(
|
|
34
|
+
connection: Connection,
|
|
35
|
+
documents: TextDocuments<TextDocument>,
|
|
36
|
+
{
|
|
37
|
+
onDidChangeConfiguration,
|
|
38
|
+
onReady,
|
|
39
|
+
}: {
|
|
40
|
+
onReady: () => void
|
|
41
|
+
onDidChangeConfiguration: (settings: PandaVSCodeSettings) => void
|
|
42
|
+
},
|
|
43
|
+
) {
|
|
44
|
+
builderResolver.onSetup(({ configPath }) => {
|
|
45
|
+
const builder = builderResolver.get(configPath)
|
|
46
|
+
if (!builder) return
|
|
47
|
+
|
|
48
|
+
const ctx = builder.context
|
|
49
|
+
if (!ctx) return
|
|
50
|
+
|
|
51
|
+
const tokenNames = Array.from(new Set(ctx.tokens.allTokens.map((token) => token.path.slice(1).join('.'))))
|
|
52
|
+
connection.sendNotification('$/panda-token-names', { configPath, tokenNames })
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
async function setupWorkspaceBuilders(rootPath: string) {
|
|
56
|
+
console.log('🐼 Setup workspace builders...')
|
|
57
|
+
const configPathList = await glob(`${rootPath}/**/panda.config.{ts,js,cjs,mjs}`, {
|
|
58
|
+
cwd: rootPath,
|
|
59
|
+
onlyFiles: true,
|
|
60
|
+
absolute: true,
|
|
61
|
+
ignore: ['**/node_modules/**'],
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
await Promise.all(
|
|
65
|
+
configPathList.map(async (configPath) => {
|
|
66
|
+
try {
|
|
67
|
+
console.log('💼 Config setup at:', configPath)
|
|
68
|
+
await builderResolver.create(configPath).setup(configPath)
|
|
69
|
+
console.log('✅ Config setup done:', configPath)
|
|
70
|
+
} catch (err) {
|
|
71
|
+
// Ignore
|
|
72
|
+
console.log('❌ Config setup failed!', configPath, err)
|
|
73
|
+
}
|
|
74
|
+
}),
|
|
75
|
+
)
|
|
76
|
+
console.log('🐼 Workspaces builders ready !')
|
|
77
|
+
|
|
78
|
+
if (configPathList.length === 1) {
|
|
79
|
+
return builderResolver.isContextSynchronizing(configPathList[0])
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function loadPandaContext(uriOrFilepath: string) {
|
|
84
|
+
const filepath = uriToPath(uriOrFilepath) ?? uriOrFilepath
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
console.log('🚧 Loading context for:', filepath)
|
|
88
|
+
ref.synchronizing = builderResolver.setup(filepath)
|
|
89
|
+
console.log('✅ Loading context done:', filepath)
|
|
90
|
+
} catch (err) {
|
|
91
|
+
// Ignore
|
|
92
|
+
ref.synchronizing = false
|
|
93
|
+
console.log('❌ Loading context failed!', err)
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
ref.synchronizing = false
|
|
98
|
+
|
|
99
|
+
const builder = builderResolver.get(filepath)
|
|
100
|
+
if (!builder || !builder.context) return
|
|
101
|
+
|
|
102
|
+
ref.context = builder.context
|
|
103
|
+
|
|
104
|
+
return ref.context
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function getClosestPandaContext(uri: string) {
|
|
108
|
+
const filepath = uriToPath(uri) ?? uri
|
|
109
|
+
|
|
110
|
+
const builder = builderResolver.get(filepath)
|
|
111
|
+
if (!builder || !builder.context) return
|
|
112
|
+
|
|
113
|
+
ref.context = builder.context
|
|
114
|
+
|
|
115
|
+
return ref.context
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const getFreshPandaSettings = async () => {
|
|
119
|
+
return getFlattenedSettings((await connection.workspace.getConfiguration('panda')) ?? defaultSettings)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Resolve current extension settings
|
|
124
|
+
*/
|
|
125
|
+
async function getPandaSettings(): Promise<PandaVSCodeSettings>
|
|
126
|
+
async function getPandaSettings<Key extends keyof PandaVSCodeSettings>(key: Key): Promise<PandaVSCodeSettings[Key]>
|
|
127
|
+
async function getPandaSettings<Key extends keyof PandaVSCodeSettings>(key?: Key) {
|
|
128
|
+
const getter = (settings: PandaVSCodeSettings) => {
|
|
129
|
+
return key ? settings[key] : settings
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!hasConfigurationCapability) {
|
|
133
|
+
return getter(defaultSettings)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!ref.settings) {
|
|
137
|
+
ref.settings = await getFreshPandaSettings()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return getter(ref.settings ?? defaultSettings)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
connection.onInitialize((params) => {
|
|
144
|
+
connection.console.log('🤖 Starting PandaCss LSP...')
|
|
145
|
+
|
|
146
|
+
const capabilities = params.capabilities
|
|
147
|
+
|
|
148
|
+
const { activeDocumentFilepath } = params.initializationOptions as { activeDocumentFilepath: string | undefined }
|
|
149
|
+
if (activeDocumentFilepath) {
|
|
150
|
+
ref.activeDocumentFilepath = activeDocumentFilepath
|
|
151
|
+
console.log('📄 Init Active document:', activeDocumentFilepath)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Check context support
|
|
155
|
+
hasConfigurationCapability = !!(capabilities.workspace && !!capabilities.workspace.configuration)
|
|
156
|
+
hasWorkspaceFolderCapability = !!(capabilities.workspace && !!capabilities.workspace.workspaceFolders)
|
|
157
|
+
|
|
158
|
+
connection.onInitialized(async () => {
|
|
159
|
+
connection.console.log('⚡ Connection initialized!')
|
|
160
|
+
|
|
161
|
+
if (hasConfigurationCapability) {
|
|
162
|
+
// Register for all configuration changes.
|
|
163
|
+
connection.client.register(DidChangeConfigurationNotification.type, undefined)
|
|
164
|
+
|
|
165
|
+
ref.settings = await getFreshPandaSettings()
|
|
166
|
+
onDidChangeConfiguration(ref.settings)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (hasWorkspaceFolderCapability) {
|
|
170
|
+
connection.workspace.onDidChangeWorkspaceFolders((_event) =>
|
|
171
|
+
connection.console.log('Workspace folder change event received.'),
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const workspaceFolders = await connection.workspace.getWorkspaceFolders()
|
|
176
|
+
const validFolders = workspaceFolders?.map((folder) => uriToPath(folder.uri) || '').filter((path) => !!path)
|
|
177
|
+
|
|
178
|
+
console.log('📁 Workspace folders:', validFolders)
|
|
179
|
+
await Promise.all(validFolders?.map((folder) => setupWorkspaceBuilders(folder)) ?? [])
|
|
180
|
+
|
|
181
|
+
onReady()
|
|
182
|
+
connection.sendNotification('$/panda-lsp-ready')
|
|
183
|
+
|
|
184
|
+
if (activeDocumentFilepath) {
|
|
185
|
+
const ctx = getClosestPandaContext(activeDocumentFilepath)
|
|
186
|
+
|
|
187
|
+
if (ctx) {
|
|
188
|
+
connection.console.log(`🐼 Found panda context! ✅ at ${ctx.path}`)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
return { capabilities: serverCapabilities }
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
connection.onNotification('$/panda-active-document-changed', (params) => {
|
|
197
|
+
console.log('📄 Active document:', ref.activeDocumentFilepath)
|
|
198
|
+
ref.activeDocumentFilepath = params.activeDocumentFilepath
|
|
199
|
+
|
|
200
|
+
const configPath = builderResolver.findConfigDirpath(ref.activeDocumentFilepath, (_, configPath) => configPath)
|
|
201
|
+
if (!configPath) return
|
|
202
|
+
|
|
203
|
+
connection.sendNotification('$/panda-doc-config-path', {
|
|
204
|
+
activeDocumentFilepath: ref.activeDocumentFilepath,
|
|
205
|
+
configPath,
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
connection.onRequest('$/get-config-path', ({ activeDocumentFilepath }: { activeDocumentFilepath: string }) => {
|
|
210
|
+
activeDocumentFilepath ??= ref.activeDocumentFilepath
|
|
211
|
+
if (!activeDocumentFilepath) return
|
|
212
|
+
|
|
213
|
+
return builderResolver.findConfigDirpath(activeDocumentFilepath, (_, configPath) => {
|
|
214
|
+
console.log('config path', configPath)
|
|
215
|
+
return configPath
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
connection.onDidChangeConfiguration(async (_change) => {
|
|
220
|
+
connection.console.log('⌛ onDidChangeConfiguration')
|
|
221
|
+
|
|
222
|
+
if (hasConfigurationCapability) {
|
|
223
|
+
ref.settings = await getFreshPandaSettings()
|
|
224
|
+
console.log('🐼 Settings changed!', ref.settings)
|
|
225
|
+
onDidChangeConfiguration(ref.settings)
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
connection.onDidChangeWatchedFiles(async ({ changes }) => {
|
|
230
|
+
changes.forEach(async (fileEvent) => {
|
|
231
|
+
const filepath = uriToPath(fileEvent.uri) ?? fileEvent.uri
|
|
232
|
+
connection.console.log('🔃 Reloading panda context for:' + filepath)
|
|
233
|
+
await builderResolver.setup(filepath)
|
|
234
|
+
})
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
documents.listen(connection)
|
|
238
|
+
connection.listen()
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
getPandaSettings,
|
|
242
|
+
loadPandaContext,
|
|
243
|
+
getContext() {
|
|
244
|
+
return ref.context
|
|
245
|
+
},
|
|
246
|
+
isSynchronizing() {
|
|
247
|
+
return ref.synchronizing
|
|
248
|
+
},
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export type PandaExtensionSetup = ReturnType<typeof setupBuilder>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ColorPresentationParams } from 'vscode-languageserver'
|
|
2
|
+
import { parseToRgba } from 'color2k'
|
|
3
|
+
|
|
4
|
+
export const color2kToVsCodeColor = (value: string) => {
|
|
5
|
+
try {
|
|
6
|
+
const [red, green, blue, alpha] = parseToRgba(value)
|
|
7
|
+
|
|
8
|
+
const color = {
|
|
9
|
+
red: red / 255,
|
|
10
|
+
green: green / 255,
|
|
11
|
+
blue: blue / 255,
|
|
12
|
+
alpha,
|
|
13
|
+
}
|
|
14
|
+
return color as ColorPresentationParams['color']
|
|
15
|
+
} catch (e) {
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type Token } from '@pandacss/token-dictionary'
|
|
2
|
+
|
|
3
|
+
type TokenFnMatch = { token: Token; index: number }
|
|
4
|
+
const tokenRegex = /token\(([^)]+)\)/g
|
|
5
|
+
|
|
6
|
+
const matchToken = (str: string, callback: (tokenPath: string, match: RegExpExecArray) => void) => {
|
|
7
|
+
let match: RegExpExecArray | null
|
|
8
|
+
|
|
9
|
+
while ((match = tokenRegex.exec(str)) != null) {
|
|
10
|
+
match[1]
|
|
11
|
+
.split(',')
|
|
12
|
+
.map((s) => s.trim())
|
|
13
|
+
.filter(Boolean)
|
|
14
|
+
.forEach((str) => callback(str, match!))
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** @see packages/core/src/plugins/expand-token-fn.ts */
|
|
19
|
+
export const expandTokenFn = (str: string, fn: (tokenName: string) => Token | undefined) => {
|
|
20
|
+
if (!str.includes('token(')) return []
|
|
21
|
+
|
|
22
|
+
const tokens = [] as TokenFnMatch[]
|
|
23
|
+
matchToken(str, (tokenPath, match) => {
|
|
24
|
+
const token = fn(tokenPath)
|
|
25
|
+
if (token) {
|
|
26
|
+
tokens.push({ token, index: match.index })
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
return tokens
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const extractTokenPaths = (str: string) => {
|
|
34
|
+
if (!str.includes('token(')) return []
|
|
35
|
+
|
|
36
|
+
const paths = [] as string[]
|
|
37
|
+
|
|
38
|
+
matchToken(str, (tokenPath) => {
|
|
39
|
+
paths.push(tokenPath)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
return paths
|
|
43
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { toCss } from '@pandacss/core'
|
|
2
|
+
import postcss from 'postcss'
|
|
3
|
+
import { type PandaContext } from '@pandacss/node'
|
|
4
|
+
|
|
5
|
+
/** @see packages/generator/src/artifacts/css/keyframe-css.ts */
|
|
6
|
+
export function generateKeyframeCss(ctx: PandaContext, keyframeName: string) {
|
|
7
|
+
const { keyframes = {} } = ctx.config.theme ?? {}
|
|
8
|
+
const root = postcss.root()
|
|
9
|
+
|
|
10
|
+
const keyframe = keyframes[keyframeName]
|
|
11
|
+
if (!keyframe) return ''
|
|
12
|
+
|
|
13
|
+
root.append(
|
|
14
|
+
postcss.atRule({
|
|
15
|
+
name: 'keyframes',
|
|
16
|
+
params: keyframeName,
|
|
17
|
+
nodes: toCss(keyframe).root.nodes,
|
|
18
|
+
}),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const rule = postcss.atRule({
|
|
22
|
+
name: 'layer',
|
|
23
|
+
params: 'tokens',
|
|
24
|
+
nodes: root.nodes,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
return rule.toString()
|
|
28
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// borrow from https://github.com/princejwesley/is-css-color/blob/master/index.js
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
// every string I match against are lowercase
|
|
5
|
+
const HEX_PATTERN = /^#(?:[a-f0-9]{3})?(?:[a-f0-9]{3})$/
|
|
6
|
+
|
|
7
|
+
// css color names + initial + inherit + currentColor + transparent
|
|
8
|
+
const CSS_COLOR_NAMES = [
|
|
9
|
+
'aliceblue',
|
|
10
|
+
'antiquewhite',
|
|
11
|
+
'aqua',
|
|
12
|
+
'aquamarine',
|
|
13
|
+
'azure',
|
|
14
|
+
'beige',
|
|
15
|
+
'bisque',
|
|
16
|
+
'black',
|
|
17
|
+
'blanchedalmond',
|
|
18
|
+
'blue',
|
|
19
|
+
'blueviolet',
|
|
20
|
+
'brown',
|
|
21
|
+
'burlywood',
|
|
22
|
+
'cadetblue',
|
|
23
|
+
'chartreuse',
|
|
24
|
+
'chocolate',
|
|
25
|
+
'coral',
|
|
26
|
+
'cornflowerblue',
|
|
27
|
+
'cornsilk',
|
|
28
|
+
'crimson',
|
|
29
|
+
'currentColor',
|
|
30
|
+
'cyan',
|
|
31
|
+
'darkblue',
|
|
32
|
+
'darkcyan',
|
|
33
|
+
'darkgoldenrod',
|
|
34
|
+
'darkgray',
|
|
35
|
+
'darkgreen',
|
|
36
|
+
'darkgrey',
|
|
37
|
+
'darkkhaki',
|
|
38
|
+
'darkmagenta',
|
|
39
|
+
'darkolivegreen',
|
|
40
|
+
'darkorange',
|
|
41
|
+
'darkorchid',
|
|
42
|
+
'darkred',
|
|
43
|
+
'darksalmon',
|
|
44
|
+
'darkseagreen',
|
|
45
|
+
'darkslateblue',
|
|
46
|
+
'darkslategray',
|
|
47
|
+
'darkslategrey',
|
|
48
|
+
'darkturquoise',
|
|
49
|
+
'darkviolet',
|
|
50
|
+
'deeppink',
|
|
51
|
+
'deepskyblue',
|
|
52
|
+
'dimgray',
|
|
53
|
+
'dimgrey',
|
|
54
|
+
'dodgerblue',
|
|
55
|
+
'firebrick',
|
|
56
|
+
'floralwhite',
|
|
57
|
+
'forestgreen',
|
|
58
|
+
'fuchsia',
|
|
59
|
+
'gainsboro',
|
|
60
|
+
'ghostwhite',
|
|
61
|
+
'gold',
|
|
62
|
+
'goldenrod',
|
|
63
|
+
'gray',
|
|
64
|
+
'green',
|
|
65
|
+
'greenyellow',
|
|
66
|
+
'grey',
|
|
67
|
+
'honeydew',
|
|
68
|
+
'hotpink',
|
|
69
|
+
'indianred',
|
|
70
|
+
'indigo',
|
|
71
|
+
'inherit',
|
|
72
|
+
'initial',
|
|
73
|
+
'ivory',
|
|
74
|
+
'khaki',
|
|
75
|
+
'lavender',
|
|
76
|
+
'lavenderblush',
|
|
77
|
+
'lawngreen',
|
|
78
|
+
'lemonchiffon',
|
|
79
|
+
'lightblue',
|
|
80
|
+
'lightcoral',
|
|
81
|
+
'lightcyan',
|
|
82
|
+
'lightgoldenrodyellow',
|
|
83
|
+
'lightgray',
|
|
84
|
+
'lightgreen',
|
|
85
|
+
'lightgrey',
|
|
86
|
+
'lightpink',
|
|
87
|
+
'lightsalmon',
|
|
88
|
+
'lightseagreen',
|
|
89
|
+
'lightskyblue',
|
|
90
|
+
'lightslategray',
|
|
91
|
+
'lightslategrey',
|
|
92
|
+
'lightsteelblue',
|
|
93
|
+
'lightyellow',
|
|
94
|
+
'lime',
|
|
95
|
+
'limegreen',
|
|
96
|
+
'linen',
|
|
97
|
+
'magenta',
|
|
98
|
+
'maroon',
|
|
99
|
+
'mediumaquamarine',
|
|
100
|
+
'mediumblue',
|
|
101
|
+
'mediumorchid',
|
|
102
|
+
'mediumpurple',
|
|
103
|
+
'mediumseagreen',
|
|
104
|
+
'mediumslateblue',
|
|
105
|
+
'mediumspringgreen',
|
|
106
|
+
'mediumturquoise',
|
|
107
|
+
'mediumvioletred',
|
|
108
|
+
'midnightblue',
|
|
109
|
+
'mintcream',
|
|
110
|
+
'mistyrose',
|
|
111
|
+
'moccasin',
|
|
112
|
+
'navajowhite',
|
|
113
|
+
'navy',
|
|
114
|
+
'oldlace',
|
|
115
|
+
'olive',
|
|
116
|
+
'olivedrab',
|
|
117
|
+
'orange',
|
|
118
|
+
'orangered',
|
|
119
|
+
'orchid',
|
|
120
|
+
'palegoldenrod',
|
|
121
|
+
'palegreen',
|
|
122
|
+
'paleturquoise',
|
|
123
|
+
'palevioletred',
|
|
124
|
+
'papayawhip',
|
|
125
|
+
'peachpuff',
|
|
126
|
+
'peru',
|
|
127
|
+
'pink',
|
|
128
|
+
'plum',
|
|
129
|
+
'powderblue',
|
|
130
|
+
'purple',
|
|
131
|
+
'rebeccapurple',
|
|
132
|
+
'red',
|
|
133
|
+
'rosybrown',
|
|
134
|
+
'royalblue',
|
|
135
|
+
'saddlebrown',
|
|
136
|
+
'salmon',
|
|
137
|
+
'sandybrown',
|
|
138
|
+
'seagreen',
|
|
139
|
+
'seashell',
|
|
140
|
+
'sienna',
|
|
141
|
+
'silver',
|
|
142
|
+
'skyblue',
|
|
143
|
+
'slateblue',
|
|
144
|
+
'slategray',
|
|
145
|
+
'slategrey',
|
|
146
|
+
'snow',
|
|
147
|
+
'springgreen',
|
|
148
|
+
'steelblue',
|
|
149
|
+
'tan',
|
|
150
|
+
'teal',
|
|
151
|
+
'thistle',
|
|
152
|
+
'tomato',
|
|
153
|
+
'transparent',
|
|
154
|
+
'turquoise',
|
|
155
|
+
'violet',
|
|
156
|
+
'wheat',
|
|
157
|
+
'white',
|
|
158
|
+
'whitesmoke',
|
|
159
|
+
'yellow',
|
|
160
|
+
'yellowgreen',
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
const PREFIX = '^(rgb|hsl)(a?)\\s*\\('
|
|
164
|
+
const VALUE = '\\s*([-+]?\\d+%?)\\s*'
|
|
165
|
+
const ALPHA = '(?:,\\s*([-+]?(?:(?:\\d+(?:.\\d+)?)|(?:.\\d+))\\s*))?'
|
|
166
|
+
const SUFFIX = '\\)$'
|
|
167
|
+
const RGB_HSL_PATTERN = new RegExp(PREFIX + VALUE + ',' + VALUE + ',' + VALUE + ALPHA + SUFFIX)
|
|
168
|
+
|
|
169
|
+
const NUM_TYPE = 1
|
|
170
|
+
const PERCENTAGE_TYPE = 2
|
|
171
|
+
const ERROR_TYPE = NUM_TYPE & PERCENTAGE_TYPE
|
|
172
|
+
|
|
173
|
+
export const isColor = (str: string) => {
|
|
174
|
+
function getColorType(token: string) {
|
|
175
|
+
return token.includes('%') ? PERCENTAGE_TYPE : NUM_TYPE
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!str || typeof str !== 'string') {
|
|
179
|
+
return false
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const color = str.replace(/^\s+|\s+$/g, '').toLocaleLowerCase()
|
|
183
|
+
|
|
184
|
+
// named colors or hex code
|
|
185
|
+
if (CSS_COLOR_NAMES.includes(color) || HEX_PATTERN.test(color)) {
|
|
186
|
+
return true
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const result = color.match(RGB_HSL_PATTERN)
|
|
190
|
+
if (result) {
|
|
191
|
+
const flavor = result[1]
|
|
192
|
+
const alpha = result[2]
|
|
193
|
+
const rh = result[3]
|
|
194
|
+
const gs = result[4]
|
|
195
|
+
const bl = result[5]
|
|
196
|
+
const a = result[6]
|
|
197
|
+
|
|
198
|
+
// alpha test
|
|
199
|
+
if ((alpha === 'a' && !a) || (a && alpha === '')) {
|
|
200
|
+
return false
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// hsl
|
|
204
|
+
if (flavor === 'hsl') {
|
|
205
|
+
if (getColorType(rh) !== NUM_TYPE) {
|
|
206
|
+
return false
|
|
207
|
+
}
|
|
208
|
+
return (getColorType(gs) & getColorType(bl)) === PERCENTAGE_TYPE
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// rgb
|
|
212
|
+
return (getColorType(rh) & getColorType(gs) & getColorType(bl)) !== ERROR_TYPE
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return false
|
|
216
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// import fs from 'fs'
|
|
2
|
+
// import path from 'path'
|
|
3
|
+
import satori from 'satori'
|
|
4
|
+
|
|
5
|
+
// __dirname = extension/dist
|
|
6
|
+
// export const Roboto = fs.readFileSync(path.resolve(__dirname, '../Roboto-Medium.ttf'))
|
|
7
|
+
export const Roboto = Buffer.from('')
|
|
8
|
+
|
|
9
|
+
// https://og-playground.vercel.app/?share=bZBBT4QwEIX_SjPG7KWJuCqHZvWyMdGzJnvhUugUupaWtEVEwn-3BS9kd04z873My7wJKisQGByE-i4MIT6MGp-nKfWENKjqJjCyu8-y2x1dl2sNSoTmKhHKd5qPkUmNP1vGtarNe8DWR1yhCei2gnPvg5Lj0UZkkvM1Ucmrr9rZ3oij1dZF1Y2UcquR8cKH-kVGHvYX4PT_V55lC5vnwryk5g21tpScrNOiMIe7mErcAwXbBWWNBzbB8jmw_VNOYQ0IWP5IQWDZ18Ak1x4pYGvP6nPsUrhhWKZ4Jpm_tiUKYMH1OFMIvIyKJvkOyRXmPw
|
|
10
|
+
export const renderFontSizePreview = (
|
|
11
|
+
text: string,
|
|
12
|
+
fontSize: string,
|
|
13
|
+
options?: {
|
|
14
|
+
width: number
|
|
15
|
+
height: number
|
|
16
|
+
},
|
|
17
|
+
) => {
|
|
18
|
+
return satori(
|
|
19
|
+
{
|
|
20
|
+
type: 'div',
|
|
21
|
+
key: null,
|
|
22
|
+
props: {
|
|
23
|
+
children: text,
|
|
24
|
+
style: {
|
|
25
|
+
height: '100%',
|
|
26
|
+
width: '100%',
|
|
27
|
+
display: 'flex',
|
|
28
|
+
alignItems: 'center',
|
|
29
|
+
justifyContent: 'center',
|
|
30
|
+
backgroundColor: '#fff',
|
|
31
|
+
fontSize: fontSize,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
width: 264,
|
|
37
|
+
height: 64,
|
|
38
|
+
...options,
|
|
39
|
+
fonts: [
|
|
40
|
+
{
|
|
41
|
+
name: 'Roboto',
|
|
42
|
+
data: Roboto,
|
|
43
|
+
weight: 400,
|
|
44
|
+
style: 'normal',
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
)
|
|
49
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type Dict } from '@pandacss/types'
|
|
2
|
+
import { type PandaContext } from '@pandacss/node'
|
|
3
|
+
import satori from 'satori'
|
|
4
|
+
import { type Token } from '@pandacss/token-dictionary'
|
|
5
|
+
import { svgToMarkdownLink } from './utils'
|
|
6
|
+
import { Roboto } from './render-font-size-preview'
|
|
7
|
+
|
|
8
|
+
// https://og-playground.vercel.app/?share=5ZTNTsMwDMdfJbLELdI2xHYIgwMfb4DEJZe2cdtAmlRJSqmqvjtNw8ZEGUzihMglsRP_9bMtp4fMCAQGWyFfuCbE-U7hVd-HMyElyqL0jHBYLZdnHGh0t1L4cuYV0tUq6YI_V_i69wfjTlrMvDQ63GZGNZXmEK6HgevrcNgBfEY4rhuVGVm920GKkEnsUG4uGANvEifdR3RYldSPu9TWB5l9U4o5Rlhpkj0X1jRa3BplbIgqLOKY8_5RpCVk8RvgL6BOy-Yk5FQ1eJR4uxiB_0XnOlTKtH-rdRbFj52L-yXXQMHUYTgdsB6m4QZ2vt5QiJDANhcUBKZNASxPlEMKWJkn-dDV4e_w7WSNMrnR_r5KUQDztsGBgk_S8UU5ldBYJWB4Aw
|
|
9
|
+
export const renderTokenColorPreview = async (ctx: PandaContext, token: Token) => {
|
|
10
|
+
const colors = [] as [tokenName: string, value: string][]
|
|
11
|
+
|
|
12
|
+
// Only bother displaying a preview for multiple colors
|
|
13
|
+
if (token.extensions.conditions) {
|
|
14
|
+
Object.entries(token.extensions.conditions).forEach(([conditionName, value]) => {
|
|
15
|
+
const [tokenRef] = ctx.tokens.getReferences(value)
|
|
16
|
+
|
|
17
|
+
colors.push([conditionName, tokenRef.value])
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
if (!colors.length) return
|
|
21
|
+
|
|
22
|
+
const svg = await satori(
|
|
23
|
+
createDiv(
|
|
24
|
+
{
|
|
25
|
+
display: 'flex',
|
|
26
|
+
flexDirection: 'column',
|
|
27
|
+
height: '100%',
|
|
28
|
+
width: '100%',
|
|
29
|
+
},
|
|
30
|
+
colors.map(([conditionName, backgroundColor]) =>
|
|
31
|
+
createDiv(
|
|
32
|
+
{ height: '100%', flex: 1, padding: '4px', display: 'flex', justifyContent: 'center', backgroundColor },
|
|
33
|
+
createDiv({ height: '100%', padding: '4px', backgroundColor: 'white', color: 'black' }, conditionName),
|
|
34
|
+
),
|
|
35
|
+
),
|
|
36
|
+
),
|
|
37
|
+
{
|
|
38
|
+
width: 256,
|
|
39
|
+
height: 28 * colors.length,
|
|
40
|
+
fonts: [
|
|
41
|
+
{
|
|
42
|
+
name: 'Roboto',
|
|
43
|
+
data: Roboto,
|
|
44
|
+
weight: 400,
|
|
45
|
+
style: 'normal',
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
return svgToMarkdownLink(svg)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const createDiv = (style: Dict, children?: DivNode) => ({ type: 'div', key: null, props: { style, children } })
|
|
55
|
+
type DivLike = {
|
|
56
|
+
type: string
|
|
57
|
+
key: null
|
|
58
|
+
props: {
|
|
59
|
+
style: Dict
|
|
60
|
+
children: any
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
type DivNode = string | DivLike | DivLike[] | null
|