@epic-web/workshop-utils 6.75.0 → 6.76.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.
@@ -1,3 +1,10 @@
1
+ export type EditorChoice = {
2
+ command: string;
3
+ label: string;
4
+ };
5
+ export declare function formatEditorLabel(command: string): string;
6
+ export declare function getAvailableEditors(): EditorChoice[];
7
+ export declare function getDefaultEditorCommand(): string | null;
1
8
  export type Result = {
2
9
  status: 'success';
3
10
  } | {
@@ -96,6 +96,34 @@ const COMMON_EDITORS_WIN = [
96
96
  'rider64.exe',
97
97
  'zed.exe',
98
98
  ];
99
+ // Map Windows executable names to their CLI commands
100
+ const WINDOWS_CLI_COMMANDS = {
101
+ 'Code.exe': 'code',
102
+ 'Cursor.exe': 'cursor',
103
+ 'Code - Insiders.exe': 'code-insiders',
104
+ 'VSCodium.exe': 'vscodium',
105
+ 'atom.exe': 'atom',
106
+ 'Brackets.exe': 'brackets',
107
+ 'sublime_text.exe': 'subl',
108
+ 'notepad++.exe': 'notepad++',
109
+ 'clion.exe': 'clion',
110
+ 'clion64.exe': 'clion',
111
+ 'idea.exe': 'idea',
112
+ 'idea64.exe': 'idea',
113
+ 'phpstorm.exe': 'phpstorm',
114
+ 'phpstorm64.exe': 'phpstorm',
115
+ 'pycharm.exe': 'pycharm',
116
+ 'pycharm64.exe': 'pycharm',
117
+ 'rubymine.exe': 'rubymine',
118
+ 'rubymine64.exe': 'rubymine',
119
+ 'webstorm.exe': 'webstorm',
120
+ 'webstorm64.exe': 'webstorm',
121
+ 'goland.exe': 'goland',
122
+ 'goland64.exe': 'goland',
123
+ 'rider.exe': 'rider',
124
+ 'rider64.exe': 'rider',
125
+ 'zed.exe': 'zed',
126
+ };
99
127
  // Transpiled version of: /^([A-Za-z]:[/\\])?[\p{L}0-9/.\-_\\]+$/u
100
128
  // Non-transpiled version requires support for Unicode property regex. Allows
101
129
  // alphanumeric characters, periods, dashes, slashes, and underscores.
@@ -256,6 +284,100 @@ function guessEditor() {
256
284
  }
257
285
  return [null];
258
286
  }
287
+ const EDITOR_LABELS = {
288
+ atom: 'Atom',
289
+ brackets: 'Brackets',
290
+ cursor: 'Cursor',
291
+ code: 'Visual Studio Code',
292
+ 'code-insiders': 'Visual Studio Code - Insiders',
293
+ vscodium: 'VSCodium',
294
+ emacs: 'Emacs',
295
+ gvim: 'GVim',
296
+ vim: 'Vim',
297
+ mvim: 'MacVim',
298
+ subl: 'Sublime Text',
299
+ sublime_text: 'Sublime Text',
300
+ 'notepad++': 'Notepad++',
301
+ clion: 'CLion',
302
+ idea: 'IntelliJ IDEA',
303
+ phpstorm: 'PhpStorm',
304
+ pycharm: 'PyCharm',
305
+ rubymine: 'RubyMine',
306
+ webstorm: 'WebStorm',
307
+ goland: 'GoLand',
308
+ rider: 'Rider',
309
+ appcode: 'AppCode',
310
+ zed: 'Zed',
311
+ };
312
+ function normalizeEditorCommand(command) {
313
+ const base = path.basename(command).replace(/\.(exe|cmd|bat)$/i, '');
314
+ return base.trim().toLowerCase();
315
+ }
316
+ export function formatEditorLabel(command) {
317
+ const normalized = normalizeEditorCommand(command);
318
+ return EDITOR_LABELS[normalized] ?? command;
319
+ }
320
+ function isCommandAvailable(command) {
321
+ if (!command)
322
+ return false;
323
+ if (path.isAbsolute(command)) {
324
+ return fs.existsSync(command);
325
+ }
326
+ if (command.includes(path.sep)) {
327
+ return fs.existsSync(command);
328
+ }
329
+ return commandExistsInPath(command);
330
+ }
331
+ function commandExistsInPath(command) {
332
+ const pathValue = process.env.PATH ?? '';
333
+ if (!pathValue)
334
+ return false;
335
+ const pathExt = process.platform === 'win32'
336
+ ? (process.env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';')
337
+ : [''];
338
+ const hasExtension = Boolean(path.extname(command));
339
+ for (const dir of pathValue.split(path.delimiter)) {
340
+ if (!dir)
341
+ continue;
342
+ for (const ext of pathExt) {
343
+ const suffix = hasExtension ? '' : ext;
344
+ const fullPath = path.join(dir, `${command}${suffix}`);
345
+ if (fs.existsSync(fullPath))
346
+ return true;
347
+ }
348
+ }
349
+ return false;
350
+ }
351
+ function getSupportedEditorCommands() {
352
+ if (process.platform === 'darwin') {
353
+ return Array.from(new Set(Object.values(COMMON_EDITORS_OSX)));
354
+ }
355
+ if (process.platform === 'win32') {
356
+ return Array.from(new Set(COMMON_EDITORS_WIN.map((exe) => WINDOWS_CLI_COMMANDS[exe] ?? exe)));
357
+ }
358
+ return Array.from(new Set(Object.values(COMMON_EDITORS_LINUX)));
359
+ }
360
+ export function getAvailableEditors() {
361
+ const available = [];
362
+ const seen = new Set();
363
+ for (const command of getSupportedEditorCommands()) {
364
+ if (!isCommandAvailable(command))
365
+ continue;
366
+ if (seen.has(command))
367
+ continue;
368
+ seen.add(command);
369
+ available.push({
370
+ command,
371
+ label: formatEditorLabel(command),
372
+ });
373
+ }
374
+ return available.sort((a, b) => a.label.localeCompare(b.label));
375
+ }
376
+ export function getDefaultEditorCommand() {
377
+ const editorInfo = guessEditor();
378
+ const editor = editorInfo[0];
379
+ return editor ? String(editor) : null;
380
+ }
259
381
  let _childProcess = null;
260
382
  export async function launchEditor(pathList, lineNumber = 1, colNumber = 1) {
261
383
  // Sanitize lineNumber to prevent malicious use on win32
@@ -3,6 +3,7 @@ export declare const PACKAGE_MANAGERS: readonly ["npm", "pnpm", "yarn", "bun"];
3
3
  export type PackageManager = (typeof PACKAGE_MANAGERS)[number];
4
4
  declare const ConfigSchema: z.ZodObject<{
5
5
  reposDirectory: z.ZodOptional<z.ZodString>;
6
+ preferredEditor: z.ZodOptional<z.ZodString>;
6
7
  }, z.core.$strip>;
7
8
  export type Workshop = {
8
9
  name: string;
@@ -19,6 +20,9 @@ export declare function getReposDirectory(): Promise<string>;
19
20
  export declare function isReposDirectoryConfigured(): Promise<boolean>;
20
21
  export declare function getDefaultReposDir(): string;
21
22
  export declare function setReposDirectory(directory: string): Promise<void>;
23
+ export declare function getPreferredEditor(): Promise<string | undefined>;
24
+ export declare function setPreferredEditor(editor: string): Promise<void>;
25
+ export declare function clearPreferredEditor(): Promise<void>;
22
26
  export type ReposDirectoryStatus = {
23
27
  accessible: true;
24
28
  } | {
@@ -23,6 +23,7 @@ export const PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun'];
23
23
  // Schema for workshop configuration (stored settings only)
24
24
  const ConfigSchema = z.object({
25
25
  reposDirectory: z.string().optional(),
26
+ preferredEditor: z.string().optional(),
26
27
  });
27
28
  function getDefaultReposDirectory() {
28
29
  return path.join(os.homedir(), 'epic-workshops');
@@ -87,6 +88,20 @@ export async function setReposDirectory(directory) {
87
88
  config.reposDirectory = path.resolve(directory);
88
89
  await saveConfig(config);
89
90
  }
91
+ export async function getPreferredEditor() {
92
+ const config = await loadConfig();
93
+ return config.preferredEditor;
94
+ }
95
+ export async function setPreferredEditor(editor) {
96
+ const config = await loadConfig();
97
+ config.preferredEditor = editor;
98
+ await saveConfig(config);
99
+ }
100
+ export async function clearPreferredEditor() {
101
+ const config = await loadConfig();
102
+ delete config.preferredEditor;
103
+ await saveConfig(config);
104
+ }
90
105
  /**
91
106
  * Verify that the configured repos directory exists and is accessible.
92
107
  * If the directory doesn't exist, attempts to create it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epic-web/workshop-utils",
3
- "version": "6.75.0",
3
+ "version": "6.76.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },