i18ntk 2.4.0 โ†’ 2.5.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.
@@ -9,25 +9,26 @@
9
9
 
10
10
  const { getIcon } = require('./terminal-icons');
11
11
  const fs = require('fs');
12
- const path = require('path');
13
- const { blue, yellow, gray, cyan, green, red } = require('./colors-new');
14
- const SecurityUtils = require('./security');
15
- const I18nSetupModule = require('../main/i18ntk-setup');
16
-
17
- async function runSetupModule() {
18
- if (I18nSetupModule && typeof I18nSetupModule.run === 'function') {
19
- return await I18nSetupModule.run();
20
- }
21
-
22
- if (typeof I18nSetupModule === 'function') {
23
- const setupManager = new I18nSetupModule();
24
- if (typeof setupManager.setup === 'function') {
25
- return await setupManager.setup();
26
- }
27
- }
28
-
29
- throw new Error('Setup module does not expose a runnable entrypoint');
30
- }
12
+ const path = require('path');
13
+ const { blue, yellow, gray, cyan, green, red } = require('./colors-new');
14
+ const SecurityUtils = require('./security');
15
+ const I18nSetupModule = require('../main/i18ntk-setup');
16
+ const { envManager } = require('./env-manager');
17
+
18
+ async function runSetupModule() {
19
+ if (I18nSetupModule && typeof I18nSetupModule.run === 'function') {
20
+ return await I18nSetupModule.run();
21
+ }
22
+
23
+ if (typeof I18nSetupModule === 'function') {
24
+ const setupManager = new I18nSetupModule();
25
+ if (typeof setupManager.setup === 'function') {
26
+ return await setupManager.setup();
27
+ }
28
+ }
29
+
30
+ throw new Error('Setup module does not expose a runnable entrypoint');
31
+ }
31
32
 
32
33
  class SetupEnforcer {
33
34
  static _setupCheckInProgress = false;
@@ -40,30 +41,30 @@ class SetupEnforcer {
40
41
  static isNonInteractive() {
41
42
  return !process.stdout.isTTY ||
42
43
  !process.stdin.isTTY ||
43
- process.env.CI === 'true' ||
44
- process.env.CONTINUOUS_INTEGRATION === 'true' ||
45
- process.env.NODE_ENV === 'test' ||
46
- process.env.npm_lifecycle_event === 'test' ||
47
- Boolean(process.env.NO_INTERACTIVE);
44
+ envManager.getBoolean('CI') ||
45
+ envManager.getBoolean('CONTINUOUS_INTEGRATION') ||
46
+ envManager.get('NODE_ENV') === 'test' ||
47
+ envManager.get('npm_lifecycle_event') === 'test' ||
48
+ envManager.getBoolean('NO_INTERACTIVE');
48
49
  }
49
50
 
50
- static checkSetupComplete() {
51
- // Avoid circular dependency - use direct path resolution
52
- const path = require('path');
53
- const configPath = path.join(process.cwd(), '.i18ntk-config');
54
- const exists = SecurityUtils.safeExistsSync(configPath);
55
- if (!exists) {
56
- this.handleMissingSetup();
57
- return;
51
+ static checkSetupComplete() {
52
+ // Avoid circular dependency - use direct path resolution
53
+ const path = require('path');
54
+ const configPath = path.join(process.cwd(), '.i18ntk-config');
55
+ const exists = SecurityUtils.safeExistsSync(configPath);
56
+ if (!exists) {
57
+ this.handleMissingSetup();
58
+ return;
58
59
  }
59
60
 
60
61
  try {
61
- const configRaw = SecurityUtils.safeReadFileSync(configPath, path.dirname(configPath), 'utf8');
62
- const config = SecurityUtils.safeParseJSON(configRaw);
63
- if (!config || typeof config !== 'object') {
64
- this.handleInvalidConfig();
65
- return;
66
- }
62
+ const configRaw = SecurityUtils.safeReadFileSync(configPath, path.dirname(configPath), 'utf8');
63
+ const config = SecurityUtils.safeParseJSON(configRaw);
64
+ if (!config || typeof config !== 'object') {
65
+ this.handleInvalidConfig();
66
+ return;
67
+ }
67
68
 
68
69
  // Check if setup has been explicitly marked as completed
69
70
  if (config.setup && config.setup.completed === true) {
@@ -123,22 +124,22 @@ class SetupEnforcer {
123
124
  process.exit(0);
124
125
  }
125
126
 
126
- console.log(green(`${getIcon('rocket')} Running setup...`));
127
-
128
- try {
129
- // Import once at module scope and execute the stable entrypoint.
130
- const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
131
- if (!SecurityUtils.safeExistsSync(setupPath)) {
132
- console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
133
- console.error(cyan(' npm run i18ntk-setup'));
134
- process.exit(1);
135
- }
136
- await runSetupModule();
137
- console.log(gray('You can now run your original command.'));
138
- resolve(true);
139
- } catch (error) {
140
- console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
141
- console.error(cyan(' npm run i18ntk-setup'));
127
+ console.log(green(`${getIcon('rocket')} Running setup...`));
128
+
129
+ try {
130
+ // Import once at module scope and execute the stable entrypoint.
131
+ const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
132
+ if (!SecurityUtils.safeExistsSync(setupPath)) {
133
+ console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
134
+ console.error(cyan(' npm run i18ntk-setup'));
135
+ process.exit(1);
136
+ }
137
+ await runSetupModule();
138
+ console.log(gray('You can now run your original command.'));
139
+ resolve(true);
140
+ } catch (error) {
141
+ console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
142
+ console.error(cyan(' npm run i18ntk-setup'));
142
143
  process.exit(1);
143
144
  }
144
145
  });
@@ -184,20 +185,20 @@ static async handleIncompleteSetup() {
184
185
  process.exit(0);
185
186
  }
186
187
 
187
- console.log(green(`${getIcon('rocket')} Running setup...`));
188
-
189
- try {
190
- const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
191
- if (!SecurityUtils.safeExistsSync(setupPath)) {
192
- console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
193
- console.error(cyan(' npm run i18ntk-setup'));
194
- process.exit(1);
195
- }
196
- await runSetupModule();
197
- resolve(true);
198
- } catch (error) {
199
- console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
200
- process.exit(1);
188
+ console.log(green(`${getIcon('rocket')} Running setup...`));
189
+
190
+ try {
191
+ const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
192
+ if (!SecurityUtils.safeExistsSync(setupPath)) {
193
+ console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
194
+ console.error(cyan(' npm run i18ntk-setup'));
195
+ process.exit(1);
196
+ }
197
+ await runSetupModule();
198
+ resolve(true);
199
+ } catch (error) {
200
+ console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
201
+ process.exit(1);
201
202
  }
202
203
  });
203
204
  });
@@ -242,20 +243,20 @@ static async handleInvalidConfig() {
242
243
  process.exit(0);
243
244
  }
244
245
 
245
- console.log(green(`${getIcon('rocket')} Running setup...`));
246
-
247
- try {
248
- const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
249
- if (!SecurityUtils.safeExistsSync(setupPath)) {
250
- console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
251
- console.error(cyan(' npm run i18ntk-setup'));
252
- process.exit(1);
253
- }
254
- await runSetupModule();
255
- resolve(true);
256
- } catch (error) {
257
- console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
258
- process.exit(1);
246
+ console.log(green(`${getIcon('rocket')} Running setup...`));
247
+
248
+ try {
249
+ const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
250
+ if (!SecurityUtils.safeExistsSync(setupPath)) {
251
+ console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
252
+ console.error(cyan(' npm run i18ntk-setup'));
253
+ process.exit(1);
254
+ }
255
+ await runSetupModule();
256
+ resolve(true);
257
+ } catch (error) {
258
+ console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
259
+ process.exit(1);
259
260
  }
260
261
  });
261
262
  });
@@ -273,28 +274,28 @@ static async handleInvalidConfig() {
273
274
  SetupEnforcer._setupCheckPromise = new Promise(async (resolve, reject) => {
274
275
  try {
275
276
  // Avoid circular dependency - use direct path resolution
276
- const path = require('path');
277
- const configPath = path.join(process.cwd(), '.i18ntk-config');
278
- const exists = SecurityUtils.safeExistsSync(configPath);
279
- if (!exists) {
280
- await SetupEnforcer.handleMissingSetup();
281
- // After setup is done, re-check the config
282
- const existsAfter = SecurityUtils.safeExistsSync(configPath);
283
- if (existsAfter) {
284
- resolve(true);
285
- } else {
277
+ const path = require('path');
278
+ const configPath = path.join(process.cwd(), '.i18ntk-config');
279
+ const exists = SecurityUtils.safeExistsSync(configPath);
280
+ if (!exists) {
281
+ await SetupEnforcer.handleMissingSetup();
282
+ // After setup is done, re-check the config
283
+ const existsAfter = SecurityUtils.safeExistsSync(configPath);
284
+ if (existsAfter) {
285
+ resolve(true);
286
+ } else {
286
287
  process.exit(0);
287
288
  }
288
289
  return;
289
290
  }
290
291
 
291
292
  try {
292
- const configRaw = SecurityUtils.safeReadFileSync(configPath, path.dirname(configPath), 'utf8');
293
- const config = SecurityUtils.safeParseJSON(configRaw);
294
- if (!config || typeof config !== 'object') {
295
- await SetupEnforcer.handleInvalidConfig();
296
- process.exit(0);
297
- }
293
+ const configRaw = SecurityUtils.safeReadFileSync(configPath, path.dirname(configPath), 'utf8');
294
+ const config = SecurityUtils.safeParseJSON(configRaw);
295
+ if (!config || typeof config !== 'object') {
296
+ await SetupEnforcer.handleInvalidConfig();
297
+ process.exit(0);
298
+ }
298
299
 
299
300
  // Check if setup has been explicitly marked as completed
300
301
  if (config.setup && config.setup.completed === true) {
@@ -306,11 +307,11 @@ static async handleInvalidConfig() {
306
307
  if (!config.version || !config.sourceDir || (!config.detectedFramework && !(config.framework && config.framework.detected !== false))) {
307
308
  await SetupEnforcer.handleIncompleteSetup();
308
309
  // After setup is done, re-check the config
309
- const newConfigRaw = SecurityUtils.safeReadFileSync(configPath, path.dirname(configPath), 'utf8');
310
- const newConfig = SecurityUtils.safeParseJSON(newConfigRaw);
311
- if (!newConfig || typeof newConfig !== 'object') {
312
- process.exit(0);
313
- }
310
+ const newConfigRaw = SecurityUtils.safeReadFileSync(configPath, path.dirname(configPath), 'utf8');
311
+ const newConfig = SecurityUtils.safeParseJSON(newConfigRaw);
312
+ if (!newConfig || typeof newConfig !== 'object') {
313
+ process.exit(0);
314
+ }
314
315
  if (newConfig.setup && newConfig.setup.completed === true) {
315
316
  resolve(true);
316
317
  } else if (newConfig.version && newConfig.sourceDir && (newConfig.detectedFramework || (newConfig.framework && newConfig.framework.detected !== false))) {
@@ -326,11 +327,11 @@ static async handleInvalidConfig() {
326
327
  await SetupEnforcer.handleInvalidConfig();
327
328
  // After setup is done, re-check the config
328
329
  try {
329
- const newConfigRaw = SecurityUtils.safeReadFileSync(configPath, path.dirname(configPath), 'utf8');
330
- const newConfig = SecurityUtils.safeParseJSON(newConfigRaw);
331
- if (!newConfig || typeof newConfig !== 'object') {
332
- process.exit(0);
333
- }
330
+ const newConfigRaw = SecurityUtils.safeReadFileSync(configPath, path.dirname(configPath), 'utf8');
331
+ const newConfig = SecurityUtils.safeParseJSON(newConfigRaw);
332
+ if (!newConfig || typeof newConfig !== 'object') {
333
+ process.exit(0);
334
+ }
334
335
  if (newConfig.setup && newConfig.setup.completed === true) {
335
336
  resolve(true);
336
337
  } else if (newConfig.version && newConfig.sourceDir && (newConfig.detectedFramework || (newConfig.framework && newConfig.framework.detected !== false))) {
@@ -353,4 +354,4 @@ static async handleInvalidConfig() {
353
354
  }
354
355
  }
355
356
 
356
- module.exports = SetupEnforcer;
357
+ module.exports = SetupEnforcer;
@@ -1,163 +1,164 @@
1
- /**
2
- * Terminal Icons Utility
3
- *
4
- * Provides Unicode emoji support with fallbacks for terminals that don't support them.
5
- * Automatically detects terminal capabilities and provides appropriate symbols.
6
- */
7
-
8
- const os = require('os');
9
-
10
-
11
- // Detect if terminal supports Unicode/emoji
12
- function supportsUnicode() {
13
- // Check if we're on Windows
14
- if (os.platform() !== 'win32') {
15
- return true; // Assume Unix-like systems support Unicode
16
- }
17
-
18
- // Check if we're in a TTY
19
- if (!process.stdout.isTTY) {
20
- return false;
21
- }
22
-
23
- try {
24
- // Try to detect Windows Terminal, VS Code terminal, or other modern terminals
25
- const terminal = process.env.TERM_PROGRAM || process.env.WT_SESSION || '';
26
-
27
- // Modern terminals that support Unicode
28
- const unicodeTerminals = [
29
- 'vscode', // VS Code integrated terminal
30
- 'hyper', // Hyper terminal
31
- 'tabby', // Tabby terminal
32
- 'fluent-terminal', // Fluent Terminal
33
- 'windows-terminal', // Windows Terminal
34
- 'wt' // Windows Terminal (WT_SESSION)
35
- ];
36
-
37
- if (unicodeTerminals.some(term => terminal.toLowerCase().includes(term))) {
38
- return true;
39
- }
40
-
41
- // Check for Windows Terminal environment variable
42
- if (process.env.WT_SESSION) {
43
- return true;
44
- }
45
-
46
- // Check for modern PowerShell or pwsh
47
- if (process.env.PSModulePath || process.env.POWERSHELL_EDITION) {
48
- return true;
49
- }
50
-
51
- // Default to false for older Windows terminals
52
- return false;
53
-
54
- } catch (error) {
55
- // If detection fails, default to safe option
56
- return false;
57
- }
58
- }
59
-
60
- // Icon definitions with fallbacks
61
- const ICONS = {
62
- // Setup and configuration
63
- wrench: { emoji: '๐Ÿ”ง', fallback: '[SETUP]' },
64
- gear: { emoji: 'โš™๏ธ', fallback: '[CONFIG]' },
65
- checkmark: { emoji: 'โœ…', fallback: '[OK]' },
66
- cross: { emoji: 'โŒ', fallback: '[ERROR]' },
67
- warning: { emoji: 'โš ๏ธ', fallback: '[WARN]' },
68
- info: { emoji: 'โ„น๏ธ', fallback: '[INFO]' },
69
-
70
- // Actions
71
- rocket: { emoji: '๐Ÿš€', fallback: '[START]' },
72
- search: { emoji: '๐Ÿ”', fallback: '[SEARCH]' },
73
- analyze: { emoji: '๐Ÿ“Š', fallback: '[ANALYZE]' },
74
- fix: { emoji: '๐Ÿ”ง', fallback: '[FIX]' },
75
- complete: { emoji: '๐ŸŽ‰', fallback: '[DONE]' },
76
- clean: { emoji: '๐Ÿงน', fallback: '[CLEAN]' },
77
-
78
- // Status
79
- success: { emoji: 'โœ…', fallback: '[SUCCESS]' },
80
- error: { emoji: 'โŒ', fallback: '[ERROR]' },
81
- pending: { emoji: 'โณ', fallback: '[PENDING]' },
82
- processing: { emoji: 'โš™๏ธ', fallback: '[WORKING]' },
83
-
84
- // Files and directories
85
- file: { emoji: '๐Ÿ“„', fallback: '[FILE]' },
86
- folder: { emoji: '๐Ÿ“', fallback: '[DIR]' },
87
- report: { emoji: '๐Ÿ“Š', fallback: '[REPORT]' },
88
- backup: { emoji: '๐Ÿ’พ', fallback: '[BACKUP]' },
89
-
90
- // Languages and frameworks
91
- javascript: { emoji: '๐ŸŸจ', fallback: '[JS]' },
92
- python: { emoji: '๐Ÿ', fallback: '[PY]' },
93
- java: { emoji: 'โ˜•', fallback: '[JAVA]' },
94
- php: { emoji: '๐Ÿ˜', fallback: '[PHP]' },
95
- go: { emoji: '๐Ÿน', fallback: '[GO]' },
96
- react: { emoji: 'โš›๏ธ', fallback: '[REACT]' },
97
- vue: { emoji: '๐Ÿ’š', fallback: '[VUE]' },
98
- angular: { emoji: '๐Ÿ…ฐ๏ธ', fallback: '[ANGULAR]' },
99
-
100
- // UI elements
101
- bullet: { emoji: 'โ€ข', fallback: '-' },
102
- arrow: { emoji: 'โ†’', fallback: '->' },
103
- separator: { emoji: 'โ•', fallback: '=' },
104
- corner: { emoji: 'โ•”', fallback: '+' },
105
- line: { emoji: 'โ•‘', fallback: '|' },
106
- end: { emoji: 'โ•š', fallback: '+' }
107
- };
108
-
109
- // Cache the Unicode support detection
110
- const _supportsUnicode = supportsUnicode();
111
-
112
- /**
113
- * Get the appropriate icon for the current terminal
114
- * @param {string} iconName - Name of the icon from ICONS
115
- * @returns {string} - The icon or fallback text
116
- */
117
- function getIcon(iconName) {
118
- const icon = ICONS[iconName];
119
- if (!icon) {
120
- return `[${iconName.toUpperCase()}]`;
121
- }
122
-
123
- return _supportsUnicode ? icon.emoji : icon.fallback;
124
- }
125
-
126
- /**
127
- * Get all available icon names
128
- * @returns {string[]} - Array of icon names
129
- */
130
- function getAvailableIcons() {
131
- return Object.keys(ICONS);
132
- }
133
-
134
- /**
135
- * Check if terminal supports Unicode
136
- * @returns {boolean} - True if Unicode is supported
137
- */
138
- function isUnicodeSupported() {
139
- return _supportsUnicode;
140
- }
141
-
142
- /**
143
- * Force enable/disable Unicode support (for testing)
144
- * @param {boolean} enabled - Whether to enable Unicode
145
- */
146
- function setUnicodeSupport(enabled) {
147
- // This is mainly for testing purposes
148
- // In production, detection should be automatic
149
- if (typeof enabled === 'boolean') {
150
- // Note: This won't actually change terminal capabilities,
151
- // just the detection result for this module
152
- console.warn('Warning: setUnicodeSupport() only affects this module\'s detection, not actual terminal capabilities');
153
- }
154
- }
155
-
156
- // Export functions
157
- module.exports = {
158
- getIcon,
159
- getAvailableIcons,
160
- isUnicodeSupported,
161
- setUnicodeSupport,
162
- ICONS
163
- };
1
+ /**
2
+ * Terminal Icons Utility
3
+ *
4
+ * Provides Unicode emoji support with fallbacks for terminals that don't support them.
5
+ * Automatically detects terminal capabilities and provides appropriate symbols.
6
+ */
7
+
8
+ const os = require('os');
9
+ const { envManager } = require('./env-manager');
10
+
11
+
12
+ // Detect if terminal supports Unicode/emoji
13
+ function supportsUnicode() {
14
+ // Check if we're on Windows
15
+ if (os.platform() !== 'win32') {
16
+ return true; // Assume Unix-like systems support Unicode
17
+ }
18
+
19
+ // Check if we're in a TTY
20
+ if (!process.stdout.isTTY) {
21
+ return false;
22
+ }
23
+
24
+ try {
25
+ // Try to detect Windows Terminal, VS Code terminal, or other modern terminals
26
+ const terminal = envManager.get('TERM_PROGRAM') || envManager.get('WT_SESSION') || '';
27
+
28
+ // Modern terminals that support Unicode
29
+ const unicodeTerminals = [
30
+ 'vscode', // VS Code integrated terminal
31
+ 'hyper', // Hyper terminal
32
+ 'tabby', // Tabby terminal
33
+ 'fluent-terminal', // Fluent Terminal
34
+ 'windows-terminal', // Windows Terminal
35
+ 'wt' // Windows Terminal (WT_SESSION)
36
+ ];
37
+
38
+ if (unicodeTerminals.some(term => terminal.toLowerCase().includes(term))) {
39
+ return true;
40
+ }
41
+
42
+ // Check for Windows Terminal environment variable
43
+ if (envManager.get('WT_SESSION')) {
44
+ return true;
45
+ }
46
+
47
+ // Check for modern PowerShell or pwsh
48
+ if (envManager.get('PSModulePath') || envManager.get('POWERSHELL_EDITION')) {
49
+ return true;
50
+ }
51
+
52
+ // Default to false for older Windows terminals
53
+ return false;
54
+
55
+ } catch (error) {
56
+ // If detection fails, default to safe option
57
+ return false;
58
+ }
59
+ }
60
+
61
+ // Icon definitions with fallbacks
62
+ const ICONS = {
63
+ // Setup and configuration
64
+ wrench: { emoji: '๐Ÿ”ง', fallback: '[SETUP]' },
65
+ gear: { emoji: 'โš™๏ธ', fallback: '[CONFIG]' },
66
+ checkmark: { emoji: 'โœ…', fallback: '[OK]' },
67
+ cross: { emoji: 'โŒ', fallback: '[ERROR]' },
68
+ warning: { emoji: 'โš ๏ธ', fallback: '[WARN]' },
69
+ info: { emoji: 'โ„น๏ธ', fallback: '[INFO]' },
70
+
71
+ // Actions
72
+ rocket: { emoji: '๐Ÿš€', fallback: '[START]' },
73
+ search: { emoji: '๐Ÿ”', fallback: '[SEARCH]' },
74
+ analyze: { emoji: '๐Ÿ“Š', fallback: '[ANALYZE]' },
75
+ fix: { emoji: '๐Ÿ”ง', fallback: '[FIX]' },
76
+ complete: { emoji: '๐ŸŽ‰', fallback: '[DONE]' },
77
+ clean: { emoji: '๐Ÿงน', fallback: '[CLEAN]' },
78
+
79
+ // Status
80
+ success: { emoji: 'โœ…', fallback: '[SUCCESS]' },
81
+ error: { emoji: 'โŒ', fallback: '[ERROR]' },
82
+ pending: { emoji: 'โณ', fallback: '[PENDING]' },
83
+ processing: { emoji: 'โš™๏ธ', fallback: '[WORKING]' },
84
+
85
+ // Files and directories
86
+ file: { emoji: '๐Ÿ“„', fallback: '[FILE]' },
87
+ folder: { emoji: '๐Ÿ“', fallback: '[DIR]' },
88
+ report: { emoji: '๐Ÿ“Š', fallback: '[REPORT]' },
89
+ backup: { emoji: '๐Ÿ’พ', fallback: '[BACKUP]' },
90
+
91
+ // Languages and frameworks
92
+ javascript: { emoji: '๐ŸŸจ', fallback: '[JS]' },
93
+ python: { emoji: '๐Ÿ', fallback: '[PY]' },
94
+ java: { emoji: 'โ˜•', fallback: '[JAVA]' },
95
+ php: { emoji: '๐Ÿ˜', fallback: '[PHP]' },
96
+ go: { emoji: '๐Ÿน', fallback: '[GO]' },
97
+ react: { emoji: 'โš›๏ธ', fallback: '[REACT]' },
98
+ vue: { emoji: '๐Ÿ’š', fallback: '[VUE]' },
99
+ angular: { emoji: '๐Ÿ…ฐ๏ธ', fallback: '[ANGULAR]' },
100
+
101
+ // UI elements
102
+ bullet: { emoji: 'โ€ข', fallback: '-' },
103
+ arrow: { emoji: 'โ†’', fallback: '->' },
104
+ separator: { emoji: 'โ•', fallback: '=' },
105
+ corner: { emoji: 'โ•”', fallback: '+' },
106
+ line: { emoji: 'โ•‘', fallback: '|' },
107
+ end: { emoji: 'โ•š', fallback: '+' }
108
+ };
109
+
110
+ // Cache the Unicode support detection
111
+ const _supportsUnicode = supportsUnicode();
112
+
113
+ /**
114
+ * Get the appropriate icon for the current terminal
115
+ * @param {string} iconName - Name of the icon from ICONS
116
+ * @returns {string} - The icon or fallback text
117
+ */
118
+ function getIcon(iconName) {
119
+ const icon = ICONS[iconName];
120
+ if (!icon) {
121
+ return `[${iconName.toUpperCase()}]`;
122
+ }
123
+
124
+ return _supportsUnicode ? icon.emoji : icon.fallback;
125
+ }
126
+
127
+ /**
128
+ * Get all available icon names
129
+ * @returns {string[]} - Array of icon names
130
+ */
131
+ function getAvailableIcons() {
132
+ return Object.keys(ICONS);
133
+ }
134
+
135
+ /**
136
+ * Check if terminal supports Unicode
137
+ * @returns {boolean} - True if Unicode is supported
138
+ */
139
+ function isUnicodeSupported() {
140
+ return _supportsUnicode;
141
+ }
142
+
143
+ /**
144
+ * Force enable/disable Unicode support (for testing)
145
+ * @param {boolean} enabled - Whether to enable Unicode
146
+ */
147
+ function setUnicodeSupport(enabled) {
148
+ // This is mainly for testing purposes
149
+ // In production, detection should be automatic
150
+ if (typeof enabled === 'boolean') {
151
+ // Note: This won't actually change terminal capabilities,
152
+ // just the detection result for this module
153
+ console.warn('Warning: setUnicodeSupport() only affects this module\'s detection, not actual terminal capabilities');
154
+ }
155
+ }
156
+
157
+ // Export functions
158
+ module.exports = {
159
+ getIcon,
160
+ getAvailableIcons,
161
+ isUnicodeSupported,
162
+ setUnicodeSupport,
163
+ ICONS
164
+ };