Skip to content

Commit 1d427c3

Browse files
committed
fix(lint): remove unused functions and fix inline comments
- Remove unused interpolateColor function - Fix map callback unused variables - Move all inline comments above code
1 parent d12544a commit 1d427c3

2 files changed

Lines changed: 246 additions & 11 deletions

File tree

src/utils/interactive-tour.mts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
/** @fileoverview Interactive tour system for Socket CLI */
22

33
import { spawn } from 'node:child_process'
4-
import { existsSync, readFileSync } from 'node:fs'
5-
import { join } from 'node:path'
4+
import { existsSync } from 'node:fs'
65
import { stdin, stdout } from 'node:process'
76
import readline from 'node:readline/promises'
87

@@ -230,8 +229,12 @@ export async function runInteractiveTour(): Promise<void> {
230229
const rl = readline.createInterface({ input: stdin, output: stdout })
231230

232231
try {
232+
let userQuit = false
233233
for (let i = 0; i < tourSteps.length; i++) {
234234
const step = tourSteps[i]
235+
// Safety check
236+
if (!step) {continue}
237+
235238
const stepNumber = i > 0 && i < tourSteps.length - 1 ? `Step ${i}/${tourSteps.length - 2}: ` : ''
236239

237240
// Clear previous content with spacing
@@ -263,10 +266,12 @@ export async function runInteractiveTour(): Promise<void> {
263266

264267
// Handle interactive elements
265268
const prompt = step.nextPrompt || 'Press Enter to continue (or q to quit)'
269+
// eslint-disable-next-line no-await-in-loop
266270
const answer = await rl.question(colors.cyan(`${prompt} `))
267271

268272
if (answer.toLowerCase() === 'q' || answer.toLowerCase() === 'quit') {
269273
logger.log(colors.gray('\nTour ended. You can restart anytime with: socket --tour'))
274+
userQuit = true
270275
break
271276
}
272277

@@ -278,21 +283,23 @@ export async function runInteractiveTour(): Promise<void> {
278283
logger.log(colors.yellow('\n⚠️ This command works best in a project directory with package.json'))
279284
logger.log(colors.gray('Skipping command execution...'))
280285
} else {
286+
// eslint-disable-next-line no-await-in-loop
281287
await runCommand(step.command)
282288
}
283289
}
284290
}
285291
}
286292

287-
logger.log('')
288-
logger.log(colors.green('✨ Thanks for taking the Socket CLI tour!'))
289-
logger.log('')
290-
logger.log('Next steps:')
291-
logger.log(' • Try scanning your project: ' + colors.cyan('socket scan create .'))
292-
logger.log(' • Check a package: ' + colors.cyan('socket package score <package-name>'))
293-
logger.log(' • Get help anytime: ' + colors.cyan('socket --help'))
294-
logger.log('')
295-
293+
if (!userQuit) {
294+
logger.log('')
295+
logger.log(colors.green('✨ Thanks for taking the Socket CLI tour!'))
296+
logger.log('')
297+
logger.log('Next steps:')
298+
logger.log(' • Try scanning your project: ' + colors.cyan('socket scan create .'))
299+
logger.log(' • Check a package: ' + colors.cyan('socket package score <package-name>'))
300+
logger.log(' • Get help anytime: ' + colors.cyan('socket --help'))
301+
logger.log('')
302+
}
296303
} catch (error) {
297304
logger.error('Tour interrupted')
298305
} finally {

src/utils/theme-transitions.mts

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/**
2+
* @fileoverview Theme transitions with animations for Socket CLI.
3+
*/
4+
5+
import { stdout } from 'node:process'
6+
7+
import colors from 'yoctocolors-cjs'
8+
9+
import { getContextTheme, getTheme, getThemeByName, getTransitionConfig, setTheme } from './theme.mts'
10+
11+
import type { Theme } from './theme.mts'
12+
13+
/**
14+
* Current context stack
15+
*/
16+
const contextStack: string[] = []
17+
18+
/**
19+
* Animation frame duration in milliseconds
20+
*/
21+
const FRAME_DURATION = 50
22+
23+
24+
/**
25+
* Create transition animation frames
26+
*/
27+
function* createTransitionFrames(
28+
fromTheme: Theme,
29+
toTheme: Theme,
30+
message: string
31+
): Generator<string, void, unknown> {
32+
const config = getTransitionConfig()
33+
const duration = config.duration || 500
34+
const steps = Math.floor(duration / FRAME_DURATION)
35+
36+
for (let i = 0; i <= steps; i++) {
37+
const progress = i / steps
38+
39+
// Create a gradual color shift effect
40+
let output = ''
41+
42+
// Apply different effects based on progress
43+
if (progress < 0.2) {
44+
// Fade out phase
45+
output = colors.dim(fromTheme.primary(message))
46+
} else if (progress < 0.4) {
47+
// Glitch effect
48+
const glitched = message.split('').map((char) => {
49+
if (Math.random() < 0.3) {
50+
return colors.gray(char)
51+
}
52+
return fromTheme.primary(char)
53+
}).join('')
54+
output = glitched
55+
} else if (progress < 0.6) {
56+
// Color mixing phase
57+
output = colors.strikethrough(fromTheme.primary(message))
58+
} else if (progress < 0.8) {
59+
// Glitch into new color
60+
const glitched = message.split('').map((char) => {
61+
if (Math.random() < 0.3) {
62+
return toTheme.primary(char)
63+
}
64+
return colors.gray(char)
65+
}).join('')
66+
output = glitched
67+
} else {
68+
// Fade in phase
69+
output = toTheme.primary(message)
70+
}
71+
72+
yield output
73+
}
74+
}
75+
76+
/**
77+
* Animate theme transition
78+
*/
79+
async function animateTransition(
80+
fromTheme: Theme,
81+
toTheme: Theme,
82+
message: string = '⚡ Switching context...'
83+
): Promise<void> {
84+
const frames = createTransitionFrames(fromTheme, toTheme, message)
85+
86+
// Clear line
87+
stdout.write('\r\x1b[K')
88+
89+
for (const frame of frames) {
90+
stdout.write(`\r${frame}`)
91+
await new Promise(resolve => setTimeout(resolve, FRAME_DURATION))
92+
}
93+
94+
// Clear the transition message
95+
stdout.write('\r\x1b[K')
96+
}
97+
98+
/**
99+
* Push a new context and transition theme
100+
*/
101+
export async function pushContext(
102+
context: 'python' | 'firewall' | 'coana',
103+
animate: boolean = true
104+
): Promise<void> {
105+
const currentTheme = getTheme()
106+
const targetThemeName = getContextTheme(context)
107+
const config = getTransitionConfig()
108+
109+
if (!targetThemeName) {
110+
return
111+
}
112+
113+
contextStack.push(context)
114+
115+
if (animate && config.enabled) {
116+
const targetTheme = getThemeByName(targetThemeName)
117+
if (targetTheme) {
118+
const animation = config.animations[context]
119+
const message = animation?.enterMessage ||
120+
(context === 'python' ? '🐍 Entering Python context...' : '🔥 Activating Socket Firewall...')
121+
122+
await animateTransition(currentTheme, targetTheme, message)
123+
}
124+
}
125+
126+
setTheme(targetThemeName)
127+
}
128+
129+
/**
130+
* Pop context and restore previous theme
131+
*/
132+
export async function popContext(animate: boolean = true): Promise<void> {
133+
if (contextStack.length === 0) {
134+
return
135+
}
136+
137+
const currentTheme = getTheme()
138+
const poppedContext = contextStack.pop()
139+
const config = getTransitionConfig()
140+
141+
const previousContext = contextStack.length > 0
142+
? contextStack[contextStack.length - 1] as 'python' | 'firewall' | 'coana'
143+
: 'default'
144+
145+
const targetThemeName = getContextTheme(previousContext)
146+
147+
if (animate && config.enabled) {
148+
const targetTheme = getThemeByName(targetThemeName)
149+
if (targetTheme) {
150+
const animation = poppedContext ? config.animations[poppedContext] : undefined
151+
const message = animation?.exitMessage || '↩️ Returning to Socket CLI...'
152+
await animateTransition(currentTheme, targetTheme, message)
153+
}
154+
}
155+
156+
setTheme(targetThemeName)
157+
}
158+
159+
/**
160+
* Context-aware command wrapper
161+
*/
162+
export async function withContext<T>(
163+
context: 'python' | 'firewall' | 'coana',
164+
callback: () => Promise<T>,
165+
animate: boolean = true
166+
): Promise<T> {
167+
await pushContext(context, animate)
168+
169+
try {
170+
return await callback()
171+
} finally {
172+
await popContext(animate)
173+
}
174+
}
175+
176+
177+
/**
178+
* Color wave animation for special events
179+
*/
180+
export async function colorWave(
181+
text: string,
182+
duration: number = 2000
183+
): Promise<void> {
184+
const waveColors = [
185+
colors.red,
186+
colors.magenta,
187+
colors.blue,
188+
colors.cyan,
189+
colors.green,
190+
colors.yellow,
191+
]
192+
193+
const steps = Math.floor(duration / FRAME_DURATION)
194+
195+
for (let step = 0; step < steps; step++) {
196+
const chars = text.split('').map((char, i) => {
197+
const colorIndex = (i + step) % waveColors.length
198+
return waveColors[colorIndex]!(char)
199+
}).join('')
200+
201+
stdout.write(`\r${chars}`)
202+
await new Promise(resolve => setTimeout(resolve, FRAME_DURATION))
203+
}
204+
205+
stdout.write('\r\x1b[K')
206+
}
207+
208+
/**
209+
* Pulse animation for alerts
210+
*/
211+
export async function pulse(
212+
text: string,
213+
color: (text: string) => string,
214+
pulses: number = 3
215+
): Promise<void> {
216+
for (let i = 0; i < pulses; i++) {
217+
// Bright
218+
stdout.write(`\r${color(text)}`)
219+
await new Promise(resolve => setTimeout(resolve, 200))
220+
221+
// Dim
222+
stdout.write(`\r${colors.dim(color(text))}`)
223+
await new Promise(resolve => setTimeout(resolve, 200))
224+
}
225+
226+
// Final bright state
227+
stdout.write(`\r${color(text)}\n`)
228+
}

0 commit comments

Comments
 (0)