@razorwind/cursor 0.0.2
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/LICENSE +201 -0
- package/README.md +348 -0
- package/dist/index.cjs +153 -0
- package/dist/index.d.cts +296 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +296 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +154 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +81 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { formatTokenValue, toCssVar } from "@razorwind/core/utils";
|
|
2
|
+
import { Schema, Tokens } from "@razorwind/core/schema";
|
|
3
|
+
import { TokenType } from "@power-plant/dtcg-schema";
|
|
4
|
+
import { GeneratorFunctionResult } from "@power-plant/core";
|
|
5
|
+
//#region src/types.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* A flattened design token ready for Cursor theme mapping.
|
|
8
|
+
*/
|
|
9
|
+
interface FlatToken {
|
|
10
|
+
/** Dot-separated token path (e.g. `color.primary`). */
|
|
11
|
+
path: string;
|
|
12
|
+
/** DTCG `$type`, when known. */
|
|
13
|
+
type?: TokenType | string;
|
|
14
|
+
/** Raw `$value` from the token document. */
|
|
15
|
+
value: unknown;
|
|
16
|
+
/** CSS-friendly string form of {@link value}. */
|
|
17
|
+
cssValue: string;
|
|
18
|
+
/** Optional DTCG `$description`. */
|
|
19
|
+
description?: string;
|
|
20
|
+
/** Theme / set id when tokens are a `Record<string, Tokens>`. */
|
|
21
|
+
theme?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* TextMate token color rule for a Cursor / VS Code theme.
|
|
25
|
+
*
|
|
26
|
+
* @see https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide
|
|
27
|
+
*/
|
|
28
|
+
interface CursorTokenColor {
|
|
29
|
+
name?: string;
|
|
30
|
+
scope?: string | string[];
|
|
31
|
+
settings: {
|
|
32
|
+
foreground?: string;
|
|
33
|
+
background?: string;
|
|
34
|
+
fontStyle?: string;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Cursor color theme document (VS Code–compatible theme JSON).
|
|
39
|
+
*
|
|
40
|
+
* Cursor is a VS Code fork and loads the same theme extension format.
|
|
41
|
+
*
|
|
42
|
+
* @see https://code.visualstudio.com/api/extension-guides/color-theme
|
|
43
|
+
* @see https://draculatheme.com/cursor
|
|
44
|
+
*/
|
|
45
|
+
interface CursorTheme {
|
|
46
|
+
/** Stable theme id used for the theme file name. */
|
|
47
|
+
name: string;
|
|
48
|
+
/** Color Theme picker label. Defaults to {@link name}. */
|
|
49
|
+
displayName?: string;
|
|
50
|
+
/** Theme kind — maps to `contributes.themes[].uiTheme`. */
|
|
51
|
+
type: "light" | "dark" | "hc" | "hcLight";
|
|
52
|
+
colors?: Record<string, string>;
|
|
53
|
+
tokenColors?: CursorTokenColor[];
|
|
54
|
+
semanticHighlighting?: boolean;
|
|
55
|
+
semanticTokenColors?: Record<string, string | {
|
|
56
|
+
foreground?: string;
|
|
57
|
+
fontStyle?: string;
|
|
58
|
+
bold?: boolean;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Map extracted design tokens to one or more Cursor theme documents.
|
|
63
|
+
*
|
|
64
|
+
* Return a single theme, an array, or a record keyed by theme id.
|
|
65
|
+
*/
|
|
66
|
+
type GenerateCursorTheme = (tokens: Tokens | Record<string, Tokens>) => CursorTheme | CursorTheme[] | Record<string, CursorTheme>;
|
|
67
|
+
/**
|
|
68
|
+
* package.json `author` field shape.
|
|
69
|
+
*/
|
|
70
|
+
type CursorPackageAuthor = string | {
|
|
71
|
+
name: string;
|
|
72
|
+
email?: string;
|
|
73
|
+
url?: string;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Options for the Razorwind Cursor theme extension generator.
|
|
77
|
+
*
|
|
78
|
+
* @see https://code.visualstudio.com/api/extension-guides/color-theme
|
|
79
|
+
* @see https://draculatheme.com/cursor
|
|
80
|
+
*/
|
|
81
|
+
interface CursorPluginOptions {
|
|
82
|
+
/**
|
|
83
|
+
* Directory (relative to the execution cwd) for the generated extension
|
|
84
|
+
* package.
|
|
85
|
+
*
|
|
86
|
+
* @defaultValue `"cursor-extension"`
|
|
87
|
+
*/
|
|
88
|
+
outputPath?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Map extracted tokens to Cursor theme JSON document(s).
|
|
91
|
+
*
|
|
92
|
+
* Required — without a mapping there is nothing to emit.
|
|
93
|
+
*/
|
|
94
|
+
mapTheme: GenerateCursorTheme;
|
|
95
|
+
/**
|
|
96
|
+
* Unscoped extension id (`contributes` / VSIX name).
|
|
97
|
+
* Must not include an npm scope.
|
|
98
|
+
*/
|
|
99
|
+
name: string;
|
|
100
|
+
/** Color Theme / extension display name. Defaults to a title-cased {@link name}. */
|
|
101
|
+
displayName?: string;
|
|
102
|
+
/** Extension short description. */
|
|
103
|
+
description?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Extension version (semver without prerelease tags — vsce rejects them).
|
|
106
|
+
*
|
|
107
|
+
* @defaultValue `"0.0.1"`
|
|
108
|
+
*/
|
|
109
|
+
version?: string;
|
|
110
|
+
/** Extension publisher id. */
|
|
111
|
+
publisher: string;
|
|
112
|
+
/**
|
|
113
|
+
* Unscoped name written into the VSIX shim when packaging.
|
|
114
|
+
* Defaults to {@link name}.
|
|
115
|
+
*/
|
|
116
|
+
extensionName?: string;
|
|
117
|
+
/**
|
|
118
|
+
* @defaultValue `"Apache-2.0"`
|
|
119
|
+
*/
|
|
120
|
+
license?: string;
|
|
121
|
+
repository?: string | {
|
|
122
|
+
type?: string;
|
|
123
|
+
url?: string;
|
|
124
|
+
directory?: string;
|
|
125
|
+
};
|
|
126
|
+
homepage?: string;
|
|
127
|
+
bugs?: string | {
|
|
128
|
+
url?: string;
|
|
129
|
+
email?: string;
|
|
130
|
+
};
|
|
131
|
+
author?: CursorPackageAuthor;
|
|
132
|
+
/**
|
|
133
|
+
* Icon path relative to the extension root (e.g. `icon.png`).
|
|
134
|
+
* The file itself is not generated — place it beside the package.
|
|
135
|
+
*/
|
|
136
|
+
icon?: string;
|
|
137
|
+
galleryBanner?: {
|
|
138
|
+
color?: string;
|
|
139
|
+
theme?: "dark" | "light";
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* @defaultValue `["Themes"]`
|
|
143
|
+
*/
|
|
144
|
+
categories?: string[];
|
|
145
|
+
keywords?: string[];
|
|
146
|
+
/**
|
|
147
|
+
* Cursor loads VS Code–compatible extensions; `engines.vscode` is required.
|
|
148
|
+
*
|
|
149
|
+
* @defaultValue `{ vscode: "^1.85.0" }`
|
|
150
|
+
*/
|
|
151
|
+
engines?: {
|
|
152
|
+
vscode: string;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Emit packaging helpers and `package.json` script entries for building a
|
|
156
|
+
* VSIX under `dist/` (Cursor install path).
|
|
157
|
+
*
|
|
158
|
+
* @defaultValue `true`
|
|
159
|
+
*
|
|
160
|
+
* @see https://draculatheme.com/cursor
|
|
161
|
+
*/
|
|
162
|
+
includeScripts?: boolean;
|
|
163
|
+
/**
|
|
164
|
+
* Extension README body. When omitted, a minimal overview is generated from
|
|
165
|
+
* the contributed themes.
|
|
166
|
+
*/
|
|
167
|
+
readme?: string;
|
|
168
|
+
/**
|
|
169
|
+
* Override body for generated `INSTALL.md`. When omitted, Cursor VSIX
|
|
170
|
+
* install steps are written (Command Palette → Install from VSIX).
|
|
171
|
+
*
|
|
172
|
+
* @see https://draculatheme.com/cursor
|
|
173
|
+
*/
|
|
174
|
+
installGuide?: string;
|
|
175
|
+
/**
|
|
176
|
+
* Restrict flattened helper tokens to these DTCG `$type` values.
|
|
177
|
+
* Does not filter what {@link mapTheme} receives.
|
|
178
|
+
*/
|
|
179
|
+
includeTypes?: TokenType[];
|
|
180
|
+
/**
|
|
181
|
+
* Extra fields merged into the generated extension `package.json`
|
|
182
|
+
* (shallow merge; `contributes.themes` / `scripts` from the plugin win).
|
|
183
|
+
*/
|
|
184
|
+
packageJson?: Record<string, unknown>;
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/flatten.d.ts
|
|
188
|
+
interface TokenSet {
|
|
189
|
+
id: string;
|
|
190
|
+
tokens: Tokens;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Split `Schema.tokens` into one or more named token sets.
|
|
194
|
+
*/
|
|
195
|
+
declare function resolveTokenSets(tokens: Tokens | Record<string, Tokens>): TokenSet[];
|
|
196
|
+
/**
|
|
197
|
+
* Flatten DTCG token trees into rows helpful for {@link CursorPluginOptions.mapTheme}.
|
|
198
|
+
*/
|
|
199
|
+
declare function flattenTokens(tokens: Tokens | Record<string, Tokens>, options?: Pick<CursorPluginOptions, "includeTypes">): FlatToken[];
|
|
200
|
+
//#endregion
|
|
201
|
+
//#region src/scripts.d.ts
|
|
202
|
+
/**
|
|
203
|
+
* Cursor-specific install guide (VSIX via Command Palette).
|
|
204
|
+
*
|
|
205
|
+
* @see https://draculatheme.com/cursor
|
|
206
|
+
*/
|
|
207
|
+
declare function renderInstallMd(options: {
|
|
208
|
+
displayName: string;
|
|
209
|
+
extensionName: string;
|
|
210
|
+
themes: Array<{
|
|
211
|
+
label: string;
|
|
212
|
+
path: string;
|
|
213
|
+
}>;
|
|
214
|
+
}): string;
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/generate.d.ts
|
|
217
|
+
/**
|
|
218
|
+
* Normalize {@link CursorPluginOptions.mapTheme} results into a theme list.
|
|
219
|
+
*/
|
|
220
|
+
declare function normalizeThemes(result: CursorTheme | CursorTheme[] | Record<string, CursorTheme>): CursorTheme[];
|
|
221
|
+
declare function renderThemeJson(theme: CursorTheme): string;
|
|
222
|
+
/**
|
|
223
|
+
* Build the extension `package.json` manifest (themes + optional scripts).
|
|
224
|
+
*
|
|
225
|
+
* When {@link themePaths} is provided, those relative paths are used for
|
|
226
|
+
* `contributes.themes[].path` (must match emitted theme files).
|
|
227
|
+
*/
|
|
228
|
+
declare function renderPackageJson(options: CursorPluginOptions, themes: CursorTheme[], themePaths?: string[]): string;
|
|
229
|
+
/**
|
|
230
|
+
* Generate a Cursor-installable theme extension package from a Razorwind schema.
|
|
231
|
+
*
|
|
232
|
+
* Emits theme JSON, `package.json`, packaging scripts, `README.md`, and
|
|
233
|
+
* `INSTALL.md` (VSIX install steps for Cursor).
|
|
234
|
+
*
|
|
235
|
+
* @see https://draculatheme.com/cursor
|
|
236
|
+
*/
|
|
237
|
+
declare function generateCursorExtension(spec: Schema, options: CursorPluginOptions): GeneratorFunctionResult<Schema, CursorPluginOptions>;
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region src/index.d.ts
|
|
240
|
+
/**
|
|
241
|
+
* Razorwind plugin that turns design tokens into a Cursor-installable theme
|
|
242
|
+
* extension (theme JSON, package.json, VSIX packaging scripts, INSTALL.md).
|
|
243
|
+
*
|
|
244
|
+
* Provide {@link CursorPluginOptions.mapTheme} to map extracted tokens to one
|
|
245
|
+
* or more VS Code–compatible theme documents. Install in Cursor via
|
|
246
|
+
* **Extensions: Install from VSIX...** (same flow as Dracula Cursor).
|
|
247
|
+
*
|
|
248
|
+
* @see https://code.visualstudio.com/api/extension-guides/color-theme
|
|
249
|
+
* @see https://draculatheme.com/cursor
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```ts
|
|
253
|
+
* import { defineConfig } from "@razorwind/core";
|
|
254
|
+
* import cursor, { flattenTokens } from "@razorwind/cursor";
|
|
255
|
+
*
|
|
256
|
+
* export default defineConfig({
|
|
257
|
+
* plugins: [
|
|
258
|
+
* cursor({
|
|
259
|
+
* name: "my-theme",
|
|
260
|
+
* publisher: "acme",
|
|
261
|
+
* displayName: "My Theme",
|
|
262
|
+
* mapTheme: tokens => {
|
|
263
|
+
* const flat = flattenTokens(tokens);
|
|
264
|
+
* const color = (path: string) =>
|
|
265
|
+
* flat.find(t => t.path === path)?.cssValue ?? "#000000";
|
|
266
|
+
*
|
|
267
|
+
* return [
|
|
268
|
+
* {
|
|
269
|
+
* name: "my-theme-dark",
|
|
270
|
+
* displayName: "My Theme Dark",
|
|
271
|
+
* type: "dark",
|
|
272
|
+
* colors: {
|
|
273
|
+
* "editor.background": color("color.bg"),
|
|
274
|
+
* "editor.foreground": color("color.fg")
|
|
275
|
+
* }
|
|
276
|
+
* },
|
|
277
|
+
* {
|
|
278
|
+
* name: "my-theme-light",
|
|
279
|
+
* displayName: "My Theme Light",
|
|
280
|
+
* type: "light",
|
|
281
|
+
* colors: {
|
|
282
|
+
* "editor.background": color("color.bg"),
|
|
283
|
+
* "editor.foreground": color("color.fg")
|
|
284
|
+
* }
|
|
285
|
+
* }
|
|
286
|
+
* ];
|
|
287
|
+
* }
|
|
288
|
+
* })
|
|
289
|
+
* ]
|
|
290
|
+
* });
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
declare const _default: (options?: CursorPluginOptions | undefined) => import("@razorwind/core/plugin").Plugin;
|
|
294
|
+
//#endregion
|
|
295
|
+
export { type CursorPluginOptions, type CursorTheme, type CursorTokenColor, type FlatToken, type GenerateCursorTheme, _default as default, flattenTokens, formatTokenValue, generateCursorExtension, normalizeThemes, renderInstallMd, renderPackageJson, renderThemeJson, resolveTokenSets, toCssVar };
|
|
296
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/flatten.ts","../src/scripts.ts","../src/generate.ts","../src/index.ts"],"mappings":""}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import{definePlugin as e}from"@razorwind/core/plugin";import{createDocument as t,formatTokenValue as n,formatTokenValue as r,isObject as i,toCssVar as a}from"@razorwind/core/utils";import{join as o}from"node:path";const s=/^(?:light|dark|dim|dimmed|high-contrast|hc|protanopia|deuteranopia|tritanopia|achromatopsia|achromatomaly|monochrome|monochromatic|grayscale|greyscale|bw|black-and-white|black-white|blackWhite|default|base|theme)(?:[A-Z]\w*|[._-].+)?$/i;function c(e){return`$value`in e||`value`in e||`$ref`in e||`ref`in e}function l(e){if(typeof e.$description==`string`)return e.$description;if(typeof e.description==`string`)return e.description}function u(e,t){return typeof e.$type==`string`?e.$type:typeof e.type==`string`?e.type:t}function d(e){if(`$value`in e)return e.$value;if(`value`in e)return e.value;if(typeof e.$ref==`string`)return e.$ref;if(typeof e.ref==`string`)return e.ref}function f(e,t,n,a,o){if(!i(e))return;let s=u(e,n);if(c(e)){let n=d(e);o.push({path:t.join(`.`),type:s,value:n,cssValue:r(n,s),description:l(e),theme:a});return}for(let[n,r]of Object.entries(e))n.startsWith(`$`)||f(r,[...t,n],s,a,o)}function p(e){if(!i(e))return[];let t=Object.keys(e).filter(e=>!e.startsWith(`$`));if(t.length>0&&t.every(e=>s.test(e))){let n=e;return t.map(e=>({id:e,tokens:n[e]}))}return[{id:`default`,tokens:e}]}function m(e,t={}){let n=t.includeTypes?new Set(t.includeTypes):void 0,r=[];for(let t of p(e)){let e=t.id==="default"?void 0:t.id;f(t.tokens,[],void 0,e,r)}return n?r.filter(e=>!e.type||n.has(e.type)):r}function h(e){return`import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
interface PackageJson {
|
|
6
|
+
name?: unknown;
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
export const packageRoot = join(scriptDir, "..");
|
|
12
|
+
const packageJsonPath = join(packageRoot, "package.json");
|
|
13
|
+
const readmePath = join(packageRoot, "README.md");
|
|
14
|
+
const vsceReadmePath = join(scriptDir, "README.package.md");
|
|
15
|
+
const readmeBackupPath = join(packageRoot, "README.md.bak");
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Temporarily rewrite package.json / README for VSIX tooling.
|
|
19
|
+
*
|
|
20
|
+
* VSIX packaging reads package.json and README.md from the extension root,
|
|
21
|
+
* while an npm-scoped package may keep a different name and README.
|
|
22
|
+
*/
|
|
23
|
+
export function withVsixPackageShim(action: () => void): void {
|
|
24
|
+
const originalPackageJson = readFileSync(packageJsonPath, "utf8");
|
|
25
|
+
const packageJson = JSON.parse(originalPackageJson) as PackageJson;
|
|
26
|
+
|
|
27
|
+
if (typeof packageJson.name !== "string") {
|
|
28
|
+
throw new TypeError("package.json must define a string name");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const originalName = packageJson.name;
|
|
32
|
+
const extensionName = ${JSON.stringify(e)};
|
|
33
|
+
packageJson.name = extensionName;
|
|
34
|
+
delete packageJson.files;
|
|
35
|
+
|
|
36
|
+
console.log(
|
|
37
|
+
\`Temporarily renaming package: \${originalName} -> \${extensionName}\\n\`
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const hadReadme = existsSync(readmePath);
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
writeFileSync(packageJsonPath, \`\${JSON.stringify(packageJson, null, 2)}\\n\`);
|
|
44
|
+
|
|
45
|
+
if (hadReadme) {
|
|
46
|
+
renameSync(readmePath, readmeBackupPath);
|
|
47
|
+
}
|
|
48
|
+
if (existsSync(vsceReadmePath)) {
|
|
49
|
+
renameSync(vsceReadmePath, readmePath);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
action();
|
|
53
|
+
} finally {
|
|
54
|
+
if (existsSync(readmePath) && existsSync(vsceReadmePath) === false) {
|
|
55
|
+
renameSync(readmePath, vsceReadmePath);
|
|
56
|
+
}
|
|
57
|
+
if (hadReadme && existsSync(readmeBackupPath)) {
|
|
58
|
+
renameSync(readmeBackupPath, readmePath);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
writeFileSync(packageJsonPath, originalPackageJson);
|
|
62
|
+
console.log(\`\\nRestored package name: \${originalName}\`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
`}function g(e){let t=`dist/${e}.vsix`;return`import { execFileSync } from "node:child_process";
|
|
66
|
+
import { mkdirSync } from "node:fs";
|
|
67
|
+
import { join } from "node:path";
|
|
68
|
+
import { packageRoot, withVsixPackageShim } from "./vsixPackageShim.ts";
|
|
69
|
+
|
|
70
|
+
mkdirSync(join(packageRoot, "dist"), { recursive: true });
|
|
71
|
+
|
|
72
|
+
withVsixPackageShim(() => {
|
|
73
|
+
execFileSync(
|
|
74
|
+
"pnpm",
|
|
75
|
+
[
|
|
76
|
+
"exec",
|
|
77
|
+
"vsce",
|
|
78
|
+
"package",
|
|
79
|
+
"--no-dependencies",
|
|
80
|
+
"--out",
|
|
81
|
+
${JSON.stringify(t)}
|
|
82
|
+
],
|
|
83
|
+
{
|
|
84
|
+
cwd: packageRoot,
|
|
85
|
+
stdio: "inherit"
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
`}function _(e){let t=e.themes.map(e=>`- ${e.label}`).join(`
|
|
90
|
+
`),n=e.repositoryUrl?`\n## Links\n\n- [GitHub](${e.repositoryUrl})\n`:``;return`# ${e.displayName}
|
|
91
|
+
|
|
92
|
+
${e.description}
|
|
93
|
+
|
|
94
|
+
## Installation
|
|
95
|
+
|
|
96
|
+
See **INSTALL.md** for Cursor VSIX install steps (\`Extensions: Install from VSIX...\`).
|
|
97
|
+
|
|
98
|
+
## Themes
|
|
99
|
+
|
|
100
|
+
${t||`- (generated themes)`}
|
|
101
|
+
${n}`}function v(e){let t=e.themes.map(e=>`- \`${e.path}\` — ${e.label}`).join(`
|
|
102
|
+
`),n=e.themes.map(e=>`- **${e.label}**`).join(`
|
|
103
|
+
`),r=`./dist/${e.extensionName}.vsix`;return`# Installing ${e.displayName}
|
|
104
|
+
|
|
105
|
+
Generated by \`@razorwind/cursor\`.
|
|
106
|
+
|
|
107
|
+
Cursor is a VS Code fork and loads VS Code–compatible theme extensions via VSIX.
|
|
108
|
+
|
|
109
|
+
## Files
|
|
110
|
+
|
|
111
|
+
- \`package.json\` — extension manifest (\`contributes.themes\`)
|
|
112
|
+
- \`themes/*.json\` — color theme documents
|
|
113
|
+
${t}
|
|
114
|
+
|
|
115
|
+
## Package VSIX
|
|
116
|
+
|
|
117
|
+
From the generated extension directory:
|
|
118
|
+
|
|
119
|
+
\`\`\`bash
|
|
120
|
+
pnpm package-vsix
|
|
121
|
+
\`\`\`
|
|
122
|
+
|
|
123
|
+
This writes \`${r}\`.
|
|
124
|
+
|
|
125
|
+
## Install in Cursor
|
|
126
|
+
|
|
127
|
+
1. Open the Command Palette (\`Ctrl+Shift+P\` / \`Cmd+Shift+P\`)
|
|
128
|
+
2. Run **Extensions: Install from VSIX...**
|
|
129
|
+
3. Select \`${r}\`
|
|
130
|
+
4. Open **Preferences: Color Theme** and pick one of:
|
|
131
|
+
|
|
132
|
+
${n}
|
|
133
|
+
|
|
134
|
+
## Select theme
|
|
135
|
+
|
|
136
|
+
\`Preferences → Color Theme\` (or Command Palette → **Color Theme**) → choose a contributed label above.
|
|
137
|
+
`}function y(){return`node_modules/**
|
|
138
|
+
src/**
|
|
139
|
+
scripts/**
|
|
140
|
+
test/**
|
|
141
|
+
tests/**
|
|
142
|
+
.github/**
|
|
143
|
+
.vscode/**
|
|
144
|
+
|
|
145
|
+
.gitignore
|
|
146
|
+
*.tsbuildinfo
|
|
147
|
+
*.vsix
|
|
148
|
+
dist/**
|
|
149
|
+
artifacts/**
|
|
150
|
+
README.md.bak
|
|
151
|
+
INSTALL.md
|
|
152
|
+
.DS_Store
|
|
153
|
+
`}const b={name:`razorwind-cursor`},x={vscode:`^1.85.0`};function S(e,n,r){return t(e,n,b,r)}function C(e){return e.split(/[-_.\s]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}function w(e){return e.trim().toLowerCase().replaceAll(/[^a-z0-9]+/g,`-`).replaceAll(/^-+|-+$/g,``)}function T(e){switch(e){case`light`:return`vs`;case`hc`:return`hc-black`;case`hcLight`:return`hc-light`;case`dark`:return`vs-dark`;default:return`vs-dark`}}function E(e){return i(e)&&typeof e.name==`string`&&(e.type===`light`||e.type===`dark`||e.type===`hc`||e.type===`hcLight`)}function D(e){if(Array.isArray(e))return e.map((e,t)=>{if(!E(e))throw TypeError(`@razorwind/cursor mapTheme()[${t}] must be a CursorTheme with name + type`);return e});if(E(e))return[e];if(!i(e))throw TypeError(`@razorwind/cursor mapTheme() must return a theme, theme array, or theme record`);return Object.entries(e).map(([e,t])=>{if(!E(t))throw TypeError(`@razorwind/cursor mapTheme()["${e}"] must be a CursorTheme with name + type`);return{...t,name:t.name||e}})}function O(e){if(typeof e==`string`)return e;if(e&&typeof e.url==`string`)return e.url}function k(e){if(!e.mapTheme)throw Error(`@razorwind/cursor requires options.mapTheme`);if(!e.name||e.name.includes(`/`))throw Error(`@razorwind/cursor requires options.name (unscoped extension id)`);if(!e.publisher)throw Error(`@razorwind/cursor requires options.publisher`)}function A(e){return e===`light`||e===`hcLight`?`light`:`dark`}function j(e){let t={name:e.name,type:A(e.type),colors:e.colors??{}};return e.displayName&&(t.displayName=e.displayName),e.tokenColors&&(t.tokenColors=e.tokenColors),e.semanticHighlighting!==void 0&&(t.semanticHighlighting=e.semanticHighlighting),e.semanticTokenColors&&(t.semanticTokenColors=e.semanticTokenColors),`${JSON.stringify(t,null,2)}\n`}function M(e,t,n){let r=e.includeScripts!==!1,a=e.displayName??C(e.name),o=e.description??`${a} — Cursor themes generated by Razorwind`,s={themes:t.map((e,t)=>({label:e.displayName??e.name,uiTheme:T(e.type),path:n?.[t]??`./themes/${w(e.name)}.json`}))},c=r?{"package-vsix":`node --import tsx scripts/buildCursorPackage.ts`}:void 0,l={name:e.name,displayName:a,description:o,version:e.version??`0.0.1`,publisher:e.publisher,license:e.license??`Apache-2.0`,categories:e.categories??[`Themes`],engines:e.engines??{...x},contributes:s,...e.keywords?{keywords:e.keywords}:{},...e.repository?{repository:e.repository}:{},...e.homepage?{homepage:e.homepage}:{},...e.bugs?{bugs:e.bugs}:{},...e.author?{author:e.author}:{},...e.icon?{icon:e.icon}:{},...e.galleryBanner?{galleryBanner:e.galleryBanner}:{},...c?{scripts:c}:{},...e.packageJson??{}};return l.name=e.name,l.publisher=e.publisher,l.contributes=s,c&&(l.scripts=i(l.scripts)&&!Array.isArray(l.scripts)?{...l.scripts,...c}:c),`${JSON.stringify(l,null,2)}\n`}function N(e,t){k(t);let n=t.outputPath??`cursor-extension`,r=t.includeScripts!==!1,i=t.extensionName??t.name,a=t.displayName??C(t.name),s=t.description??`${a} — Cursor themes generated by Razorwind`,c=D(t.mapTheme(e.tokens));if(c.length===0)throw Error(`@razorwind/cursor mapTheme() returned no themes`);let l={},u=new Set,d=[];for(let e of c){let t=w(e.name);if(u.has(t)){let n=2;for(;u.has(`${w(e.name)}-${n}`);)n+=1;t=`${w(e.name)}-${n}`}u.add(t);let r=`${t}.json`,i=o(n,`themes`,r);l[i]=S(i,j(e),`json`),d.push({label:e.displayName??e.name,path:`themes/${r}`,slug:t})}let f=o(n,`package.json`);l[f]=S(f,M(t,c,d.map(e=>`./${e.path}`)),`json`);let p=o(n,`.vscodeignore`);l[p]=S(p,y(),`ignore`);let m=d.map(e=>({label:e.label})),b=t.readme??_({displayName:a,description:s,themes:m,repositoryUrl:O(t.repository)}),x=o(n,`README.md`);l[x]=S(x,b,`markdown`);let T=t.installGuide??v({displayName:a,extensionName:i,themes:d.map(e=>({label:e.label,path:e.path}))}),E=o(n,`INSTALL.md`);if(l[E]=S(E,T,`markdown`),r){let e=o(n,`scripts`,`vsixPackageShim.ts`);l[e]=S(e,h(i),`typescript`);let r=o(n,`scripts`,`buildCursorPackage.ts`);l[r]=S(r,g(i),`typescript`);let c=o(n,`scripts`,`README.package.md`);l[c]=S(c,_({displayName:a,description:s,themes:m,repositoryUrl:O(t.repository)}),`markdown`)}return l}var P=e(e=>({name:`cursor`,generate:async t=>{if(!e)throw Error(`@razorwind/cursor requires options: { name, publisher, mapTheme }`);return N(t,e)}}));export{P as default,m as flattenTokens,n as formatTokenValue,N as generateCursorExtension,D as normalizeThemes,v as renderInstallMd,M as renderPackageJson,j as renderThemeJson,p as resolveTokenSets,a as toCssVar};
|
|
154
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@razorwind/cursor",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Razorwind Cursor theme extension plugin that creates VSIX themes from design tokens.",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "github",
|
|
8
|
+
"url": "https://github.com/storm-software/razorwind.git",
|
|
9
|
+
"directory": "packages/cursor"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://stormsoftware.com",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://stormsoftware.com/support",
|
|
14
|
+
"email": "support@stormsoftware.com"
|
|
15
|
+
},
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "Storm Software",
|
|
18
|
+
"email": "contact@stormsoftware.com",
|
|
19
|
+
"url": "https://stormsoftware.com"
|
|
20
|
+
},
|
|
21
|
+
"license": "Apache-2.0",
|
|
22
|
+
"private": false,
|
|
23
|
+
"files": ["dist"],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"cursor",
|
|
26
|
+
"vscode",
|
|
27
|
+
"vsce",
|
|
28
|
+
"theme",
|
|
29
|
+
"razorwind",
|
|
30
|
+
"design-tokens",
|
|
31
|
+
"dtcg",
|
|
32
|
+
"storm-software"
|
|
33
|
+
],
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"require": {
|
|
37
|
+
"types": "./dist/index.d.cts",
|
|
38
|
+
"default": "./dist/index.cjs"
|
|
39
|
+
},
|
|
40
|
+
"import": {
|
|
41
|
+
"types": "./dist/index.d.mts",
|
|
42
|
+
"default": "./dist/index.mjs"
|
|
43
|
+
},
|
|
44
|
+
"default": {
|
|
45
|
+
"types": "./dist/index.d.mts",
|
|
46
|
+
"default": "./dist/index.mjs"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"./generate": {
|
|
50
|
+
"require": {
|
|
51
|
+
"types": "./dist/index.d.cts",
|
|
52
|
+
"default": "./dist/index.cjs"
|
|
53
|
+
},
|
|
54
|
+
"import": {
|
|
55
|
+
"types": "./dist/index.d.mts",
|
|
56
|
+
"default": "./dist/index.mjs"
|
|
57
|
+
},
|
|
58
|
+
"default": {
|
|
59
|
+
"types": "./dist/index.d.mts",
|
|
60
|
+
"default": "./dist/index.mjs"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"./package.json": "./package.json"
|
|
64
|
+
},
|
|
65
|
+
"main": "./dist/index.cjs",
|
|
66
|
+
"module": "./dist/index.mjs",
|
|
67
|
+
"types": "./dist/index.d.cts",
|
|
68
|
+
"typings": "dist/index.d.mts",
|
|
69
|
+
"dependencies": {
|
|
70
|
+
"@power-plant/core": "^0.0.75",
|
|
71
|
+
"@power-plant/dtcg-schema": "^0.0.1",
|
|
72
|
+
"@razorwind/core": "0.0.27"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@powerlines/plugin-tsdown": "^0.1.589",
|
|
76
|
+
"@types/node": "^25.9.5",
|
|
77
|
+
"typescript": "^6.0.3"
|
|
78
|
+
},
|
|
79
|
+
"publishConfig": { "access": "public" },
|
|
80
|
+
"gitHead": "556f2499e0ef94c0463e86c2d8f9b42bbdb0bc08"
|
|
81
|
+
}
|