semicons 0.0.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/.turbo/turbo-build.log +10 -0
- package/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/dist/extension.js +827 -0
- package/dist/extension.js.map +7 -0
- package/esbuild.js +26 -0
- package/package.json +75 -0
- package/src/extension.ts +78 -0
- package/src/features/commands.ts +149 -0
- package/src/features/completion.ts +105 -0
- package/src/features/diagnostics.ts +105 -0
- package/src/features/hover.ts +90 -0
- package/src/registry/cache.ts +71 -0
- package/src/registry/loader.ts +26 -0
- package/src/registry/types.ts +38 -0
- package/src/registry/watcher.ts +67 -0
- package/src/utils/config.ts +17 -0
- package/src/utils/path.ts +49 -0
- package/src/utils/tokenParse.ts +79 -0
- package/src/views/previewWebview.ts +253 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,827 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/extension.ts
|
|
21
|
+
var extension_exports = {};
|
|
22
|
+
__export(extension_exports, {
|
|
23
|
+
activate: () => activate,
|
|
24
|
+
deactivate: () => deactivate
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(extension_exports);
|
|
27
|
+
var import_vscode12 = require("vscode");
|
|
28
|
+
|
|
29
|
+
// src/registry/watcher.ts
|
|
30
|
+
var import_vscode3 = require("vscode");
|
|
31
|
+
|
|
32
|
+
// src/registry/cache.ts
|
|
33
|
+
var import_vscode2 = require("vscode");
|
|
34
|
+
|
|
35
|
+
// src/registry/loader.ts
|
|
36
|
+
var import_vscode = require("vscode");
|
|
37
|
+
async function loadRegistry(folderUri) {
|
|
38
|
+
const config = import_vscode.workspace.getConfiguration("semicons", folderUri);
|
|
39
|
+
const registryPath = config.get("registryPath", "src/icons.generated/registry.json");
|
|
40
|
+
const registryUri = import_vscode.Uri.joinPath(folderUri, registryPath);
|
|
41
|
+
try {
|
|
42
|
+
const document = await import_vscode.workspace.openTextDocument(registryUri);
|
|
43
|
+
const content = document.getText();
|
|
44
|
+
const registry = JSON.parse(content);
|
|
45
|
+
return registry;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function resolveRegistryPath(folderUri) {
|
|
51
|
+
const config = import_vscode.workspace.getConfiguration("semicons", folderUri);
|
|
52
|
+
const registryPath = config.get("registryPath", "src/icons.generated/registry.json");
|
|
53
|
+
return import_vscode.Uri.joinPath(folderUri, registryPath);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/registry/cache.ts
|
|
57
|
+
var cache = {};
|
|
58
|
+
async function getRegistryForFile(fileUri) {
|
|
59
|
+
const folder = import_vscode2.workspace.getWorkspaceFolder(fileUri);
|
|
60
|
+
if (folder) {
|
|
61
|
+
const folderKey = folder.uri.toString();
|
|
62
|
+
if (!cache[folderKey]) {
|
|
63
|
+
cache[folderKey] = await loadRegistry(folder.uri);
|
|
64
|
+
}
|
|
65
|
+
return cache[folderKey];
|
|
66
|
+
}
|
|
67
|
+
const folders = import_vscode2.workspace.workspaceFolders;
|
|
68
|
+
if (folders && folders.length > 0) {
|
|
69
|
+
const firstFolder = folders[0];
|
|
70
|
+
const firstKey = firstFolder.uri.toString();
|
|
71
|
+
if (!cache[firstKey]) {
|
|
72
|
+
cache[firstKey] = await loadRegistry(firstFolder.uri);
|
|
73
|
+
}
|
|
74
|
+
return cache[firstKey];
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
function setRegistry(folderUri, registry) {
|
|
79
|
+
const folderKey = folderUri.toString();
|
|
80
|
+
cache[folderKey] = registry;
|
|
81
|
+
}
|
|
82
|
+
function clearCache() {
|
|
83
|
+
for (const key of Object.keys(cache)) {
|
|
84
|
+
delete cache[key];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function getAllRegistries() {
|
|
88
|
+
const registries = [];
|
|
89
|
+
for (const value of Object.values(cache)) {
|
|
90
|
+
if (value && !(value instanceof Promise)) {
|
|
91
|
+
registries.push(value);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return registries;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/registry/watcher.ts
|
|
98
|
+
var watchers = /* @__PURE__ */ new Map();
|
|
99
|
+
function setupRegistryWatcher() {
|
|
100
|
+
const folders = import_vscode3.workspace.workspaceFolders;
|
|
101
|
+
if (!folders) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
for (const folder of folders) {
|
|
105
|
+
const registryUri = resolveRegistryPath(folder.uri);
|
|
106
|
+
const watcherKey = folder.uri.toString();
|
|
107
|
+
if (watchers.has(watcherKey)) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const watcher = import_vscode3.workspace.createFileSystemWatcher(
|
|
111
|
+
registryUri.fsPath,
|
|
112
|
+
false,
|
|
113
|
+
false,
|
|
114
|
+
false
|
|
115
|
+
);
|
|
116
|
+
watcher.onDidChange(async () => {
|
|
117
|
+
const registry = await loadRegistry(folder.uri);
|
|
118
|
+
setRegistry(folder.uri, registry);
|
|
119
|
+
});
|
|
120
|
+
watcher.onDidCreate(async () => {
|
|
121
|
+
const registry = await loadRegistry(folder.uri);
|
|
122
|
+
setRegistry(folder.uri, registry);
|
|
123
|
+
});
|
|
124
|
+
watcher.onDidDelete(() => {
|
|
125
|
+
setRegistry(folder.uri, null);
|
|
126
|
+
});
|
|
127
|
+
watchers.set(watcherKey, watcher);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function refreshAllRegistries() {
|
|
131
|
+
clearCache();
|
|
132
|
+
const folders = import_vscode3.workspace.workspaceFolders;
|
|
133
|
+
if (!folders) {
|
|
134
|
+
return Promise.resolve();
|
|
135
|
+
}
|
|
136
|
+
const promises = folders.map(async (folder) => {
|
|
137
|
+
const registry = await loadRegistry(folder.uri);
|
|
138
|
+
setRegistry(folder.uri, registry);
|
|
139
|
+
});
|
|
140
|
+
return Promise.all(promises).then(() => void 0);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/features/completion.ts
|
|
144
|
+
var import_vscode5 = require("vscode");
|
|
145
|
+
|
|
146
|
+
// src/utils/config.ts
|
|
147
|
+
var import_vscode4 = require("vscode");
|
|
148
|
+
function getSemiconsConfig(folder) {
|
|
149
|
+
const config = import_vscode4.workspace.getConfiguration("semicons", folder);
|
|
150
|
+
return {
|
|
151
|
+
registryPath: config.get("registryPath", "src/icons.generated/registry.json"),
|
|
152
|
+
localIconDir: config.get("localIconDir", "icons/local"),
|
|
153
|
+
iconComponentName: config.get("iconComponentName", "Icon")
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/features/completion.ts
|
|
158
|
+
function registerCompletionProvider() {
|
|
159
|
+
const provider = import_vscode5.languages.registerCompletionItemProvider(
|
|
160
|
+
[
|
|
161
|
+
{ language: "typescriptreact" },
|
|
162
|
+
{ language: "javascriptreact" },
|
|
163
|
+
{ language: "vue" },
|
|
164
|
+
{ language: "astro" }
|
|
165
|
+
],
|
|
166
|
+
{
|
|
167
|
+
async provideCompletionItems(document, position, token) {
|
|
168
|
+
const registry = await getRegistryForFile(document.uri);
|
|
169
|
+
if (!registry || !registry.tokens) {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
const config = getSemiconsConfig();
|
|
173
|
+
const iconName = config.iconComponentName;
|
|
174
|
+
const line = document.lineAt(position.line).text;
|
|
175
|
+
const linePrefix = line.substring(0, position.character);
|
|
176
|
+
const iconPattern = new RegExp(`<${iconName}\\s+[^>]*name\\s*=\\s*["']`, "g");
|
|
177
|
+
const vuePattern = new RegExp(`:${iconName}\\s*:\\s*name\\s*=\\s*["']`, "g");
|
|
178
|
+
const isInIconName = iconPattern.test(linePrefix) || vuePattern.test(linePrefix);
|
|
179
|
+
if (!isInIconName) {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
const items = [];
|
|
183
|
+
for (const token2 of registry.tokens) {
|
|
184
|
+
const deprecated = token2.meta.deprecated;
|
|
185
|
+
const category = token2.name.split(":")[0];
|
|
186
|
+
const item = new import_vscode5.CompletionItem(
|
|
187
|
+
token2.name,
|
|
188
|
+
import_vscode5.CompletionItemKind.EnumMember
|
|
189
|
+
);
|
|
190
|
+
let detail = category;
|
|
191
|
+
if (deprecated) {
|
|
192
|
+
detail += " (deprecated)";
|
|
193
|
+
}
|
|
194
|
+
item.detail = detail;
|
|
195
|
+
const docLines = [];
|
|
196
|
+
if (token2.a11y?.label) {
|
|
197
|
+
docLines.push(`a11y: "${token2.a11y.label}"`);
|
|
198
|
+
}
|
|
199
|
+
if (token2.meta.description) {
|
|
200
|
+
docLines.push(token2.meta.description);
|
|
201
|
+
}
|
|
202
|
+
if (deprecated) {
|
|
203
|
+
docLines.push(`\u26A0\uFE0F Deprecated: ${typeof deprecated === "string" ? deprecated : "This icon is deprecated"}`);
|
|
204
|
+
}
|
|
205
|
+
const doc = new import_vscode5.MarkdownString();
|
|
206
|
+
doc.value = docLines.join("\n\n");
|
|
207
|
+
item.documentation = doc;
|
|
208
|
+
item.insertText = token2.name;
|
|
209
|
+
item.range = void 0;
|
|
210
|
+
items.push(item);
|
|
211
|
+
}
|
|
212
|
+
return items;
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
'"',
|
|
216
|
+
// Trigger on quote
|
|
217
|
+
"'"
|
|
218
|
+
// Trigger on single quote
|
|
219
|
+
);
|
|
220
|
+
return provider;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// src/features/hover.ts
|
|
224
|
+
var import_vscode7 = require("vscode");
|
|
225
|
+
|
|
226
|
+
// src/utils/tokenParse.ts
|
|
227
|
+
var import_vscode6 = require("vscode");
|
|
228
|
+
var ICON_TAG_REGEX = /<Icon\b/gi;
|
|
229
|
+
var NAME_ATTR_REGEX = /\s+name\s*=\s*["']([^"']+)["']/g;
|
|
230
|
+
var VUE_NAME_ATTR_REGEX = /\s+:name\s*=\s*["']([^"']+)["']/g;
|
|
231
|
+
function findIconTokens(document) {
|
|
232
|
+
const text = document.getText();
|
|
233
|
+
const matches = [];
|
|
234
|
+
let match;
|
|
235
|
+
while ((match = ICON_TAG_REGEX.exec(text)) !== null) {
|
|
236
|
+
const tagStart = match.index;
|
|
237
|
+
const tagEnd = tagStart + match[0].length;
|
|
238
|
+
const remainingText = text.substring(tagEnd);
|
|
239
|
+
let nameMatch;
|
|
240
|
+
const nameRegex = new RegExp(NAME_ATTR_REGEX);
|
|
241
|
+
while ((nameMatch = nameRegex.exec(remainingText)) !== null) {
|
|
242
|
+
const nameStart = tagEnd + nameMatch.index;
|
|
243
|
+
const nameEnd = nameStart + nameMatch[0].length;
|
|
244
|
+
const nameValue = nameMatch[1];
|
|
245
|
+
const startPos = document.positionAt(nameStart);
|
|
246
|
+
const endPos = document.positionAt(nameEnd);
|
|
247
|
+
matches.push({
|
|
248
|
+
name: nameValue,
|
|
249
|
+
range: new import_vscode6.Range(startPos, endPos),
|
|
250
|
+
isAttributeValue: true
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
const vueNameRegex = new RegExp(VUE_NAME_ATTR_REGEX);
|
|
254
|
+
while ((nameMatch = vueNameRegex.exec(remainingText)) !== null) {
|
|
255
|
+
const nameStart = tagEnd + nameMatch.index;
|
|
256
|
+
const nameEnd = nameStart + nameMatch[0].length;
|
|
257
|
+
const nameValue = nameMatch[1];
|
|
258
|
+
const startPos = document.positionAt(nameStart);
|
|
259
|
+
const endPos = document.positionAt(nameEnd);
|
|
260
|
+
matches.push({
|
|
261
|
+
name: nameValue,
|
|
262
|
+
range: new import_vscode6.Range(startPos, endPos),
|
|
263
|
+
isAttributeValue: true
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return matches;
|
|
268
|
+
}
|
|
269
|
+
function extractTokenNameAtPosition(document, position) {
|
|
270
|
+
const matches = findIconTokens(document);
|
|
271
|
+
for (const match of matches) {
|
|
272
|
+
if (match.range.contains(position)) {
|
|
273
|
+
return match.name;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/features/hover.ts
|
|
280
|
+
function registerHoverProvider() {
|
|
281
|
+
const provider = import_vscode7.languages.registerHoverProvider(
|
|
282
|
+
[
|
|
283
|
+
{ language: "typescriptreact" },
|
|
284
|
+
{ language: "javascriptreact" },
|
|
285
|
+
{ language: "vue" },
|
|
286
|
+
{ language: "astro" }
|
|
287
|
+
],
|
|
288
|
+
{
|
|
289
|
+
async provideHover(document, position, cancellationToken) {
|
|
290
|
+
const registry = await getRegistryForFile(document.uri);
|
|
291
|
+
if (!registry || !registry.tokens) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const tokenName = extractTokenNameAtPosition(document, position);
|
|
295
|
+
if (!tokenName) {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
const foundToken = registry.tokens.find((t) => t.name === tokenName);
|
|
299
|
+
if (!foundToken) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
const markdown = new import_vscode7.MarkdownString();
|
|
303
|
+
markdown.isTrusted = true;
|
|
304
|
+
const deprecated = foundToken.meta.deprecated;
|
|
305
|
+
const theme = registry.defaultTheme;
|
|
306
|
+
const assetRef = foundToken.themes[theme] || Object.values(foundToken.themes)[0];
|
|
307
|
+
const lines = [];
|
|
308
|
+
if (deprecated) {
|
|
309
|
+
lines.push(`~~${foundToken.name}~~ \u26A0\uFE0F`);
|
|
310
|
+
} else {
|
|
311
|
+
lines.push(`**${foundToken.name}**`);
|
|
312
|
+
}
|
|
313
|
+
lines.push(`\`${assetRef}\``);
|
|
314
|
+
if (foundToken.a11y?.label) {
|
|
315
|
+
lines.push(`a11y: "${foundToken.a11y.label}"`);
|
|
316
|
+
}
|
|
317
|
+
if (foundToken.meta.tags && foundToken.meta.tags.length > 0) {
|
|
318
|
+
lines.push(`tags: ${foundToken.meta.tags.join(", ")}`);
|
|
319
|
+
}
|
|
320
|
+
const previewCommand = `[Preview icon](command:semicons.previewIcon?${encodeURIComponent(JSON.stringify([foundToken.name]))})`;
|
|
321
|
+
lines.push("", previewCommand);
|
|
322
|
+
markdown.value = lines.join("\n\n");
|
|
323
|
+
return new import_vscode7.Hover(markdown);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
);
|
|
327
|
+
return provider;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// src/features/diagnostics.ts
|
|
331
|
+
var import_vscode8 = require("vscode");
|
|
332
|
+
var TOKEN_PATTERN = /^[a-z][a-z0-9-]*:[a-zA-Z0-9][a-zA-Z0-9._/-]*$/;
|
|
333
|
+
var DiagnosticsManager = class {
|
|
334
|
+
collection;
|
|
335
|
+
disposables = [];
|
|
336
|
+
constructor() {
|
|
337
|
+
this.collection = import_vscode8.languages.createDiagnosticCollection("semicons");
|
|
338
|
+
}
|
|
339
|
+
async updateDocument(document) {
|
|
340
|
+
const registry = await getRegistryForFile(document.uri);
|
|
341
|
+
const diagnostics = [];
|
|
342
|
+
const matches = findIconTokens(document);
|
|
343
|
+
for (const match of matches) {
|
|
344
|
+
const { name, range } = match;
|
|
345
|
+
if (!TOKEN_PATTERN.test(name)) {
|
|
346
|
+
diagnostics.push(new import_vscode8.Diagnostic(
|
|
347
|
+
range,
|
|
348
|
+
`Invalid token format. Expected "category:name" (e.g., "navigation:menu")`,
|
|
349
|
+
import_vscode8.DiagnosticSeverity.Error
|
|
350
|
+
));
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (!registry || !registry.tokens) {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
const token = registry.tokens.find((t) => t.name === name);
|
|
357
|
+
if (!token) {
|
|
358
|
+
diagnostics.push(new import_vscode8.Diagnostic(
|
|
359
|
+
range,
|
|
360
|
+
`Icon token "${name}" not found in registry`,
|
|
361
|
+
import_vscode8.DiagnosticSeverity.Error
|
|
362
|
+
));
|
|
363
|
+
} else if (token.meta.deprecated) {
|
|
364
|
+
const deprecationMessage = typeof token.meta.deprecated === "string" ? `Deprecated: ${token.meta.deprecated}` : "This icon is deprecated";
|
|
365
|
+
diagnostics.push(new import_vscode8.Diagnostic(
|
|
366
|
+
range,
|
|
367
|
+
`\u26A0\uFE0F ${deprecationMessage}`,
|
|
368
|
+
import_vscode8.DiagnosticSeverity.Warning
|
|
369
|
+
));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
this.collection.set(document.uri, diagnostics);
|
|
373
|
+
}
|
|
374
|
+
clear() {
|
|
375
|
+
this.collection.clear();
|
|
376
|
+
}
|
|
377
|
+
dispose() {
|
|
378
|
+
this.collection.dispose();
|
|
379
|
+
for (const d of this.disposables) {
|
|
380
|
+
d.dispose();
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
function registerDiagnosticsProvider(manager) {
|
|
385
|
+
const disposables2 = [];
|
|
386
|
+
const languagesToRegister = [
|
|
387
|
+
"typescriptreact",
|
|
388
|
+
"javascriptreact",
|
|
389
|
+
"vue",
|
|
390
|
+
"astro"
|
|
391
|
+
];
|
|
392
|
+
for (const language of languagesToRegister) {
|
|
393
|
+
const pattern = `**/*.${language === "typescriptreact" || language === "javascriptreact" ? "tsx" : language === "vue" ? "vue" : "astro"}`;
|
|
394
|
+
const watcher = import_vscode8.workspace.createFileSystemWatcher(pattern);
|
|
395
|
+
watcher.onDidChange(async (uri) => {
|
|
396
|
+
const document = await import_vscode8.workspace.openTextDocument(uri);
|
|
397
|
+
await manager.updateDocument(document);
|
|
398
|
+
});
|
|
399
|
+
disposables2.push(watcher);
|
|
400
|
+
}
|
|
401
|
+
return import_vscode8.Disposable.from(...disposables2);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/features/commands.ts
|
|
405
|
+
var import_vscode11 = require("vscode");
|
|
406
|
+
|
|
407
|
+
// src/views/previewWebview.ts
|
|
408
|
+
var import_vscode10 = require("vscode");
|
|
409
|
+
|
|
410
|
+
// src/utils/path.ts
|
|
411
|
+
var import_vscode9 = require("vscode");
|
|
412
|
+
function parseAssetRef(assetRef) {
|
|
413
|
+
const match = assetRef.match(/^([^:]+):(.+)$/);
|
|
414
|
+
if (!match) {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
return { namespace: match[1], id: match[2] };
|
|
418
|
+
}
|
|
419
|
+
async function resolveAssetToFileUri(assetRef, folder) {
|
|
420
|
+
const parsed = parseAssetRef(assetRef);
|
|
421
|
+
if (!parsed) {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
const { namespace, id } = parsed;
|
|
425
|
+
if (namespace === "local") {
|
|
426
|
+
const config = getSemiconsConfig(folder);
|
|
427
|
+
const filePath = `${config.localIconDir}/${id}.svg`;
|
|
428
|
+
const fileUri = import_vscode9.Uri.joinPath(folder, filePath);
|
|
429
|
+
try {
|
|
430
|
+
await import_vscode9.workspace.fs.stat(fileUri);
|
|
431
|
+
return fileUri;
|
|
432
|
+
} catch {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
async function readSvgContent(fileUri) {
|
|
439
|
+
try {
|
|
440
|
+
const bytes = await import_vscode9.workspace.fs.readFile(fileUri);
|
|
441
|
+
return new TextDecoder().decode(bytes);
|
|
442
|
+
} catch {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// src/views/previewWebview.ts
|
|
448
|
+
var PreviewWebview = class {
|
|
449
|
+
panel = null;
|
|
450
|
+
state = null;
|
|
451
|
+
async show(token, registry, folderUri) {
|
|
452
|
+
this.state = { token, registry, folderUri };
|
|
453
|
+
if (this.panel) {
|
|
454
|
+
this.panel.reveal(import_vscode10.ViewColumn.Beside);
|
|
455
|
+
this.updatePanel();
|
|
456
|
+
} else {
|
|
457
|
+
this.panel = import_vscode10.window.createWebviewPanel(
|
|
458
|
+
"semiconsPreview",
|
|
459
|
+
`Preview: ${token.name}`,
|
|
460
|
+
import_vscode10.ViewColumn.Beside,
|
|
461
|
+
{
|
|
462
|
+
enableScripts: true,
|
|
463
|
+
localResourceRoots: [folderUri]
|
|
464
|
+
}
|
|
465
|
+
);
|
|
466
|
+
this.panel.onDidDispose(() => {
|
|
467
|
+
this.panel = null;
|
|
468
|
+
this.state = null;
|
|
469
|
+
});
|
|
470
|
+
this.updatePanel();
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
async updatePanel() {
|
|
474
|
+
if (!this.panel || !this.state) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
const { token, registry, folderUri } = this.state;
|
|
478
|
+
const theme = registry.defaultTheme;
|
|
479
|
+
const assetRef = token.themes[theme] || Object.values(token.themes)[0];
|
|
480
|
+
const fileUri = await resolveAssetToFileUri(assetRef, folderUri);
|
|
481
|
+
let svgContent = null;
|
|
482
|
+
let svgExists = false;
|
|
483
|
+
if (fileUri) {
|
|
484
|
+
svgContent = await readSvgContent(fileUri);
|
|
485
|
+
svgExists = svgContent !== null;
|
|
486
|
+
}
|
|
487
|
+
const previewHtml = svgExists && svgContent ? this.wrapSvg(svgContent) : this.createPlaceholder(token.name, assetRef, svgExists);
|
|
488
|
+
const html = this.buildHtml(token, registry, assetRef, previewHtml);
|
|
489
|
+
this.panel.webview.html = html;
|
|
490
|
+
}
|
|
491
|
+
wrapSvg(svg) {
|
|
492
|
+
return svg.replace(/\s+width="[^"]*"/g, "").replace(/\s+height="[^"]*"/g, "").replace(/\s+viewBox="[^"]*"/g, "").replace(/^<svg/, '<svg width="100%" height="100%" viewBox="0 0 24 24"');
|
|
493
|
+
}
|
|
494
|
+
createPlaceholder(name, assetRef, fileExists) {
|
|
495
|
+
return `
|
|
496
|
+
<div class="placeholder">
|
|
497
|
+
<div class="placeholder-icon">
|
|
498
|
+
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
499
|
+
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
|
500
|
+
<line x1="9" y1="9" x2="15" y2="15"/>
|
|
501
|
+
<line x1="15" y1="9" x2="9" y2="15"/>
|
|
502
|
+
</svg>
|
|
503
|
+
</div>
|
|
504
|
+
<p class="placeholder-text">${fileExists ? "Preview unavailable" : "File not found"}</p>
|
|
505
|
+
<p class="placeholder-ref">${assetRef}</p>
|
|
506
|
+
${!fileExists ? `<p class="placeholder-hint">Expected: ${name.replace("local:", "icons/local/") + ".svg"}</p>` : ""}
|
|
507
|
+
</div>
|
|
508
|
+
`;
|
|
509
|
+
}
|
|
510
|
+
buildHtml(token, registry, assetRef, previewHtml) {
|
|
511
|
+
const deprecated = token.meta.deprecated;
|
|
512
|
+
return `<!DOCTYPE html>
|
|
513
|
+
<html>
|
|
514
|
+
<head>
|
|
515
|
+
<meta charset="UTF-8">
|
|
516
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
517
|
+
<title>${token.name}</title>
|
|
518
|
+
<style>
|
|
519
|
+
body {
|
|
520
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
521
|
+
margin: 0;
|
|
522
|
+
padding: 16px;
|
|
523
|
+
background: #1e1e2e;
|
|
524
|
+
color: #cdd6f4;
|
|
525
|
+
}
|
|
526
|
+
.header {
|
|
527
|
+
margin-bottom: 16px;
|
|
528
|
+
}
|
|
529
|
+
.token-name {
|
|
530
|
+
font-size: 18px;
|
|
531
|
+
font-weight: 600;
|
|
532
|
+
margin: 0;
|
|
533
|
+
}
|
|
534
|
+
.token-name.deprecated {
|
|
535
|
+
text-decoration: line-through;
|
|
536
|
+
opacity: 0.7;
|
|
537
|
+
}
|
|
538
|
+
.token-ref {
|
|
539
|
+
font-family: 'Fira Code', monospace;
|
|
540
|
+
font-size: 12px;
|
|
541
|
+
color: #a6adc8;
|
|
542
|
+
background: #313244;
|
|
543
|
+
padding: 2px 8px;
|
|
544
|
+
border-radius: 4px;
|
|
545
|
+
}
|
|
546
|
+
.section {
|
|
547
|
+
margin-bottom: 16px;
|
|
548
|
+
}
|
|
549
|
+
.section-title {
|
|
550
|
+
font-size: 12px;
|
|
551
|
+
text-transform: uppercase;
|
|
552
|
+
color: #6c7086;
|
|
553
|
+
margin-bottom: 8px;
|
|
554
|
+
}
|
|
555
|
+
.preview-container {
|
|
556
|
+
background: #181825;
|
|
557
|
+
border-radius: 8px;
|
|
558
|
+
padding: 24px;
|
|
559
|
+
display: flex;
|
|
560
|
+
justify-content: center;
|
|
561
|
+
align-items: center;
|
|
562
|
+
min-height: 120px;
|
|
563
|
+
}
|
|
564
|
+
.preview-container svg {
|
|
565
|
+
max-width: 100%;
|
|
566
|
+
max-height: 100px;
|
|
567
|
+
}
|
|
568
|
+
.placeholder {
|
|
569
|
+
text-align: center;
|
|
570
|
+
padding: 24px;
|
|
571
|
+
}
|
|
572
|
+
.placeholder-icon {
|
|
573
|
+
opacity: 0.3;
|
|
574
|
+
margin-bottom: 8px;
|
|
575
|
+
}
|
|
576
|
+
.placeholder-text {
|
|
577
|
+
font-size: 14px;
|
|
578
|
+
color: #a6adc8;
|
|
579
|
+
margin: 0 0 4px 0;
|
|
580
|
+
}
|
|
581
|
+
.placeholder-ref {
|
|
582
|
+
font-family: 'Fira Code', monospace;
|
|
583
|
+
font-size: 11px;
|
|
584
|
+
color: #6c7086;
|
|
585
|
+
margin: 0 0 8px 0;
|
|
586
|
+
}
|
|
587
|
+
.placeholder-hint {
|
|
588
|
+
font-size: 11px;
|
|
589
|
+
color: #f38ba8;
|
|
590
|
+
margin: 0;
|
|
591
|
+
}
|
|
592
|
+
.meta-row {
|
|
593
|
+
display: flex;
|
|
594
|
+
gap: 8px;
|
|
595
|
+
flex-wrap: wrap;
|
|
596
|
+
}
|
|
597
|
+
.tag {
|
|
598
|
+
font-size: 11px;
|
|
599
|
+
background: #313244;
|
|
600
|
+
padding: 2px 8px;
|
|
601
|
+
border-radius: 4px;
|
|
602
|
+
}
|
|
603
|
+
.warning {
|
|
604
|
+
background: #f9e2af;
|
|
605
|
+
color: #1e1e2e;
|
|
606
|
+
padding: 8px 12px;
|
|
607
|
+
border-radius: 4px;
|
|
608
|
+
font-size: 12px;
|
|
609
|
+
margin-top: 8px;
|
|
610
|
+
}
|
|
611
|
+
</style>
|
|
612
|
+
</head>
|
|
613
|
+
<body>
|
|
614
|
+
<div class="header">
|
|
615
|
+
<h1 class="token-name ${deprecated ? "deprecated" : ""}">${token.name}</h1>
|
|
616
|
+
<span class="token-ref">${assetRef}</span>
|
|
617
|
+
</div>
|
|
618
|
+
|
|
619
|
+
${deprecated ? `<div class="warning">\u26A0\uFE0F Deprecated: ${typeof deprecated === "string" ? deprecated : "This icon is deprecated"}</div>` : ""}
|
|
620
|
+
|
|
621
|
+
<div class="section">
|
|
622
|
+
<div class="section-title">Preview</div>
|
|
623
|
+
<div class="preview-container">
|
|
624
|
+
${previewHtml}
|
|
625
|
+
</div>
|
|
626
|
+
</div>
|
|
627
|
+
|
|
628
|
+
${token.a11y?.label ? `
|
|
629
|
+
<div class="section">
|
|
630
|
+
<div class="section-title">Accessibility</div>
|
|
631
|
+
<div>aria-label: "${token.a11y.label}"</div>
|
|
632
|
+
</div>
|
|
633
|
+
` : ""}
|
|
634
|
+
|
|
635
|
+
${token.meta.description ? `
|
|
636
|
+
<div class="section">
|
|
637
|
+
<div class="section-title">Description</div>
|
|
638
|
+
<div>${token.meta.description}</div>
|
|
639
|
+
</div>
|
|
640
|
+
` : ""}
|
|
641
|
+
|
|
642
|
+
${token.meta.tags && token.meta.tags.length > 0 ? `
|
|
643
|
+
<div class="section">
|
|
644
|
+
<div class="section-title">Tags</div>
|
|
645
|
+
<div class="meta-row">
|
|
646
|
+
${token.meta.tags.map((tag) => `<span class="tag">${tag}</span>`).join("")}
|
|
647
|
+
</div>
|
|
648
|
+
</div>
|
|
649
|
+
` : ""}
|
|
650
|
+
|
|
651
|
+
<div class="section">
|
|
652
|
+
<div class="section-title">Usage</div>
|
|
653
|
+
<pre style="background: #313244; padding: 12px; border-radius: 6px; overflow-x: auto;"><code><Icon name="${token.name}" /></code></pre>
|
|
654
|
+
</div>
|
|
655
|
+
</body>
|
|
656
|
+
</html>`;
|
|
657
|
+
}
|
|
658
|
+
dispose() {
|
|
659
|
+
if (this.panel) {
|
|
660
|
+
this.panel.dispose();
|
|
661
|
+
this.panel = null;
|
|
662
|
+
}
|
|
663
|
+
this.state = null;
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
// src/features/commands.ts
|
|
668
|
+
var previewWebview = new PreviewWebview();
|
|
669
|
+
function registerCommands() {
|
|
670
|
+
const disposables2 = [];
|
|
671
|
+
disposables2.push(import_vscode11.commands.registerCommand("semicons.refreshRegistry", async () => {
|
|
672
|
+
await refreshAllRegistries();
|
|
673
|
+
import_vscode11.window.showInformationMessage("Semicons registry refreshed");
|
|
674
|
+
}));
|
|
675
|
+
disposables2.push(import_vscode11.commands.registerCommand("semicons.searchToken", async () => {
|
|
676
|
+
const registries = await getAllRegistries();
|
|
677
|
+
if (registries.length === 0) {
|
|
678
|
+
import_vscode11.window.showWarningMessage("No registry found. Run pnpm semicons generate first.");
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
const tokens = [];
|
|
682
|
+
for (const registry of registries) {
|
|
683
|
+
if (registry && registry.tokens) {
|
|
684
|
+
for (const token2 of registry.tokens) {
|
|
685
|
+
tokens.push({ name: token2.name, registry });
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
const items = tokens.map((t) => ({
|
|
690
|
+
label: t.name,
|
|
691
|
+
detail: t.name.split(":")[0],
|
|
692
|
+
description: t.registry.tokens.find((tok) => tok.name === t.name)?.meta.deprecated ? "(deprecated)" : void 0
|
|
693
|
+
}));
|
|
694
|
+
const selected = await import_vscode11.window.showQuickPick(items, {
|
|
695
|
+
placeHolder: "Search for an icon token...",
|
|
696
|
+
matchOnDescription: true,
|
|
697
|
+
matchOnDetail: true
|
|
698
|
+
});
|
|
699
|
+
if (!selected) {
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
const tokenName = selected.label;
|
|
703
|
+
let foundRegistry = null;
|
|
704
|
+
let foundFolder;
|
|
705
|
+
for (const folder of import_vscode11.workspace.workspaceFolders || []) {
|
|
706
|
+
const registry = await getRegistryForFile(folder.uri);
|
|
707
|
+
if (registry?.tokens.some((t) => t.name === tokenName)) {
|
|
708
|
+
foundRegistry = registry;
|
|
709
|
+
foundFolder = folder.uri;
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (!foundRegistry || !foundFolder) {
|
|
714
|
+
import_vscode11.window.showErrorMessage("Token registry not found");
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
const token = foundRegistry.tokens.find((t) => t.name === tokenName);
|
|
718
|
+
if (!token) {
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
const editor = import_vscode11.window.activeTextEditor;
|
|
722
|
+
if (editor) {
|
|
723
|
+
const insertText = `name="${tokenName}"`;
|
|
724
|
+
editor.edit((editBuilder) => {
|
|
725
|
+
editBuilder.insert(editor.selection.active, insertText);
|
|
726
|
+
});
|
|
727
|
+
} else {
|
|
728
|
+
await import_vscode11.commands.executeCommand("editor.action.clipboardCopyAction");
|
|
729
|
+
import_vscode11.window.showInformationMessage(`Token "${tokenName}" copied to clipboard`);
|
|
730
|
+
}
|
|
731
|
+
}));
|
|
732
|
+
disposables2.push(import_vscode11.commands.registerCommand("semicons.previewIcon", async (tokenName) => {
|
|
733
|
+
if (!tokenName) {
|
|
734
|
+
const editor = import_vscode11.window.activeTextEditor;
|
|
735
|
+
if (editor) {
|
|
736
|
+
const token = await getTokenAtCursor(editor.document, editor.selection.active);
|
|
737
|
+
if (token) {
|
|
738
|
+
tokenName = token;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
if (!tokenName) {
|
|
743
|
+
import_vscode11.window.showWarningMessage("No token found at cursor");
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
const registries = await getAllRegistries();
|
|
747
|
+
for (const registry of registries) {
|
|
748
|
+
if (registry?.tokens) {
|
|
749
|
+
const token = registry.tokens.find((t) => t.name === tokenName);
|
|
750
|
+
if (token) {
|
|
751
|
+
const folder = import_vscode11.workspace.workspaceFolders?.find(async (f) => {
|
|
752
|
+
const allRegs = await getAllRegistries();
|
|
753
|
+
const reg = allRegs.find((r) => r === registry);
|
|
754
|
+
return reg !== void 0;
|
|
755
|
+
});
|
|
756
|
+
if (folder) {
|
|
757
|
+
await previewWebview.show(token, registry, folder.uri);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
import_vscode11.window.showErrorMessage(`Token "${tokenName}" not found in registry`);
|
|
764
|
+
}));
|
|
765
|
+
return import_vscode11.Disposable.from(...disposables2);
|
|
766
|
+
}
|
|
767
|
+
async function getTokenAtCursor(document, position) {
|
|
768
|
+
const line = document.getText().split("\n")[position.line];
|
|
769
|
+
const match = line.match(/name\s*=\s*["']([^"']+)["']/);
|
|
770
|
+
return match ? match[1] : null;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// src/extension.ts
|
|
774
|
+
var diagnosticsManager = null;
|
|
775
|
+
var disposables = [];
|
|
776
|
+
async function activate(context) {
|
|
777
|
+
await refreshAllRegistries();
|
|
778
|
+
setupRegistryWatcher();
|
|
779
|
+
disposables.push(registerCompletionProvider());
|
|
780
|
+
disposables.push(registerHoverProvider());
|
|
781
|
+
diagnosticsManager = new DiagnosticsManager();
|
|
782
|
+
disposables.push(registerDiagnosticsProvider(diagnosticsManager));
|
|
783
|
+
disposables.push(registerCommands());
|
|
784
|
+
const registries = await getAllRegistries();
|
|
785
|
+
if (registries.length > 0) {
|
|
786
|
+
import_vscode12.window.setStatusBarMessage("Semicons loaded", 3e3);
|
|
787
|
+
} else {
|
|
788
|
+
import_vscode12.window.showWarningMessage(
|
|
789
|
+
'Semicons: No registry found. Run "pnpm semicons generate" to create registry.json',
|
|
790
|
+
"Generate Now"
|
|
791
|
+
).then((action) => {
|
|
792
|
+
if (action === "Generate Now") {
|
|
793
|
+
import_vscode12.commands.executeCommand("workbench.action.terminal.runSelectedText");
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
import_vscode12.workspace.onDidChangeTextDocument((event) => {
|
|
798
|
+
if (diagnosticsManager) {
|
|
799
|
+
diagnosticsManager.updateDocument(event.document);
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
import_vscode12.window.onDidChangeActiveTextEditor((editor) => {
|
|
803
|
+
if (editor && diagnosticsManager) {
|
|
804
|
+
diagnosticsManager.updateDocument(editor.document);
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
context.subscriptions.push({
|
|
808
|
+
dispose: () => {
|
|
809
|
+
for (const d of disposables) {
|
|
810
|
+
d.dispose();
|
|
811
|
+
}
|
|
812
|
+
disposables.length = 0;
|
|
813
|
+
if (diagnosticsManager) {
|
|
814
|
+
diagnosticsManager.dispose();
|
|
815
|
+
diagnosticsManager = null;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
function deactivate() {
|
|
821
|
+
}
|
|
822
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
823
|
+
0 && (module.exports = {
|
|
824
|
+
activate,
|
|
825
|
+
deactivate
|
|
826
|
+
});
|
|
827
|
+
//# sourceMappingURL=extension.js.map
|