plugin-gentleman 1.0.2 → 1.0.3
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/package.json +1 -1
- package/tui.tsx +86 -92
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "plugin-gentleman",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.3",
|
|
5
5
|
"description": "OpenCode TUI plugin featuring Mustachi - an animated ASCII mascot with eyes, mustache, and optional motivational phrases during busy states",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
package/tui.tsx
CHANGED
|
@@ -6,60 +6,41 @@ import { createSignal, onCleanup, createEffect } from "solid-js"
|
|
|
6
6
|
|
|
7
7
|
const id = "gentleman"
|
|
8
8
|
|
|
9
|
-
// Premium Mustachi ASCII art -
|
|
9
|
+
// Premium Mustachi ASCII art - compact version for sidebar (25 chars wide)
|
|
10
10
|
// Base structure with eyes that will be replaced dynamically
|
|
11
11
|
const mustachiNeutralBase = [
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
" ██░░░░████████░░░░██ ██░░░░░░░░░░░░░░░░██",
|
|
18
|
-
" ██░░░░░░░░░░░░░░██ ██░░░░░░░░░░░░░░██",
|
|
19
|
-
" ██░░░░░░░░░░░░██ ██░░░░░░░░░░░░██",
|
|
20
|
-
" ████████████ ████████████",
|
|
12
|
+
" █████ █████",
|
|
13
|
+
" ██░░░░░██ ██░░░░░██",
|
|
14
|
+
" ██░░███░░██ ██░░░░░░░██",
|
|
15
|
+
" ██░░███░░██ ██░░░░░░░██",
|
|
16
|
+
"██ ██░░░░░██ ██░░░░░██ ██",
|
|
21
17
|
]
|
|
22
18
|
|
|
23
19
|
// Squinted eyes version for busy state
|
|
24
20
|
const mustachiSquintedBase = [
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
" ██░░░░░░░░░░░░░░░░██ ██░░░░░░░░░░░░░░░░██",
|
|
31
|
-
" ██░░░░░░░░░░░░░░██ ██░░░░░░░░░░░░░░██",
|
|
32
|
-
" ██░░░░░░░░░░░░██ ██░░░░░░░░░░░░██",
|
|
33
|
-
" ████████████ ████████████",
|
|
21
|
+
" █████ █████",
|
|
22
|
+
" ██░░░░░██ ██░░░░░██",
|
|
23
|
+
" ██░░███░░██ ██░░░░░░░██",
|
|
24
|
+
" █████████ █████████",
|
|
25
|
+
"██ █████ █████ ██",
|
|
34
26
|
]
|
|
35
27
|
|
|
36
|
-
// Mustache section (
|
|
28
|
+
// Mustache section (compact 25-char wide design)
|
|
37
29
|
const mustachiMustacheSection = [
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
" ███████████████████████████████████████████████████████████",
|
|
44
|
-
" ███████████████████████████████████████████████████████████",
|
|
45
|
-
" ███████████████████████████████████████████████████████████",
|
|
46
|
-
" █████████████████████████████████████████████████████████",
|
|
47
|
-
" ███████████████████████████████████████████████████████",
|
|
48
|
-
" ▓▓█████████████████████ █████████████████████▓▓",
|
|
49
|
-
" ▓▓▓███████████████ ███████████████▓▓▓",
|
|
50
|
-
" ▓▓▓█████████ █████████▓▓▓",
|
|
51
|
-
" ▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓",
|
|
30
|
+
"██████████ ████████",
|
|
31
|
+
"████████████ ██████████",
|
|
32
|
+
" █████████████████████████",
|
|
33
|
+
" ▓██████████ ██████████▓",
|
|
34
|
+
" ▓██████ ██████▓",
|
|
52
35
|
]
|
|
53
36
|
|
|
54
|
-
// Tongue animation frames (progressive)
|
|
37
|
+
// Tongue animation frames (progressive) - compact design
|
|
55
38
|
const tongueFrames = [
|
|
56
39
|
[], // no tongue
|
|
57
|
-
["
|
|
58
|
-
[" ███████", " █████"], // medium tongue
|
|
59
|
-
[" ███████", " █████", " ███"], // full tongue
|
|
40
|
+
[" ███", " █"], // tongue out
|
|
60
41
|
]
|
|
61
42
|
|
|
62
|
-
// Mustache-only ASCII art for home logo (
|
|
43
|
+
// Mustache-only ASCII art for home logo (original massive solid block design)
|
|
63
44
|
const mustachiMustacheOnly = [
|
|
64
45
|
"",
|
|
65
46
|
" ████████ ████████",
|
|
@@ -82,11 +63,12 @@ const mustachiMustacheOnly = [
|
|
|
82
63
|
// Left pupil positions for look-around animation (progressive)
|
|
83
64
|
// Modifies only the left eye (white sclera with dark pupil)
|
|
84
65
|
// Right eye is monocle/glass and remains static
|
|
66
|
+
// Pupil is on lines 2 and 3 (indices 2-3) of the 5-line eye array
|
|
85
67
|
const leftPupilPositions = [
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
68
|
+
"██░░███░░██", // center (line 2 of eyes)
|
|
69
|
+
"██████░░░██", // looking left
|
|
70
|
+
"██░░░██████", // looking right
|
|
71
|
+
"██░░███░░██", // center again
|
|
90
72
|
]
|
|
91
73
|
|
|
92
74
|
// Blink animation frames (progressive) - affects both eyes
|
|
@@ -96,30 +78,22 @@ const blinkFrames = [
|
|
|
96
78
|
// Half closed
|
|
97
79
|
{
|
|
98
80
|
left: [
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
" ██░░░░████████░░░░██ ██░░░░░░░░░░░░░░░░██",
|
|
105
|
-
" ██░░░░░░░░░░░░░░██ ██░░░░░░░░░░░░░░██",
|
|
106
|
-
" ██░░░░░░░░░░░░██ ██░░░░░░░░░░░░██",
|
|
107
|
-
" ████████████ ████████████",
|
|
81
|
+
" █████ █████",
|
|
82
|
+
" ██░░░░░██ ██░░░░░██",
|
|
83
|
+
" ██░░███░░██ ██░░░░░░░██",
|
|
84
|
+
" █████████ █████████",
|
|
85
|
+
"██ █████ █████ ██",
|
|
108
86
|
],
|
|
109
87
|
squinted: mustachiSquintedBase // squinted stays squinted during blink
|
|
110
88
|
},
|
|
111
89
|
// Fully closed
|
|
112
90
|
{
|
|
113
91
|
left: [
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
" ██░░████████████░░██ ██░░░░░░░░░░░░░░░░██",
|
|
120
|
-
" ██░░░░░░░░░░░░░░██ ██░░░░░░░░░░░░░░██",
|
|
121
|
-
" ██░░░░░░░░░░░░██ ██░░░░░░░░░░░░██",
|
|
122
|
-
" ████████████ ████████████",
|
|
92
|
+
" █████ █████",
|
|
93
|
+
" ██░░░░░██ ██░░░░░██",
|
|
94
|
+
" █████████ █████████",
|
|
95
|
+
" █████████ █████████",
|
|
96
|
+
"██ █████ █████ ██",
|
|
123
97
|
],
|
|
124
98
|
squinted: mustachiSquintedBase
|
|
125
99
|
},
|
|
@@ -235,31 +209,40 @@ const getProviders = (providers: ReadonlyArray<{ id: string; name: string }> | u
|
|
|
235
209
|
return Array.from(names).sort().join(", ")
|
|
236
210
|
}
|
|
237
211
|
|
|
238
|
-
// Home logo: Mustache-only (simple and prominent)
|
|
212
|
+
// Home logo: Mustache-only (simple and prominent) with grayscale gradient
|
|
239
213
|
const HomeLogo = (props: { theme: TuiThemeCurrent }) => {
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
const
|
|
214
|
+
// Grayscale palette for better TUI readability
|
|
215
|
+
const lightGray = "#C0C0C0" // Light gray for highlights
|
|
216
|
+
const midGray = "#808080" // Mid gray for main body
|
|
217
|
+
const darkGray = "#505050" // Dark gray for shadows
|
|
243
218
|
|
|
244
219
|
return (
|
|
245
220
|
<box flexDirection="column" alignItems="center">
|
|
246
|
-
{/* Mustache
|
|
221
|
+
{/* Mustache with grayscale gradient for depth */}
|
|
247
222
|
{mustachiMustacheOnly.map((line, idx) => {
|
|
248
223
|
const totalLines = mustachiMustacheOnly.length
|
|
249
|
-
let color =
|
|
224
|
+
let color = midGray
|
|
250
225
|
if (idx < totalLines / 3) {
|
|
251
|
-
color =
|
|
226
|
+
color = lightGray // Top highlight
|
|
252
227
|
} else if (idx >= (2 * totalLines) / 3) {
|
|
253
|
-
color =
|
|
228
|
+
color = darkGray // Bottom shadow
|
|
254
229
|
}
|
|
255
230
|
return <text fg={color}>{line}</text>
|
|
256
231
|
})}
|
|
257
232
|
|
|
258
|
-
{/* OpenCode branding */}
|
|
259
|
-
<box flexDirection="
|
|
260
|
-
<
|
|
261
|
-
|
|
262
|
-
|
|
233
|
+
{/* OpenCode branding — enlarged and prominent */}
|
|
234
|
+
<box flexDirection="column" alignItems="center" marginTop={1}>
|
|
235
|
+
<box flexDirection="row" gap={0}>
|
|
236
|
+
<text fg={props.theme.textMuted}>╔═══════════╗</text>
|
|
237
|
+
</box>
|
|
238
|
+
<box flexDirection="row" gap={0}>
|
|
239
|
+
<text fg={props.theme.textMuted}>║ </text>
|
|
240
|
+
<text fg={props.theme.primary} bold={true}>OpenCode</text>
|
|
241
|
+
<text fg={props.theme.textMuted}> ║</text>
|
|
242
|
+
</box>
|
|
243
|
+
<box flexDirection="row" gap={0}>
|
|
244
|
+
<text fg={props.theme.textMuted}>╚═══════════╝</text>
|
|
245
|
+
</box>
|
|
263
246
|
</box>
|
|
264
247
|
|
|
265
248
|
<text> </text>
|
|
@@ -267,7 +250,7 @@ const HomeLogo = (props: { theme: TuiThemeCurrent }) => {
|
|
|
267
250
|
)
|
|
268
251
|
}
|
|
269
252
|
|
|
270
|
-
// Sidebar: Full Mustachi face with progressive animations
|
|
253
|
+
// Sidebar: Full Mustachi face with progressive animations (grayscale for clarity)
|
|
271
254
|
const SidebarMustachi = (props: { theme: TuiThemeCurrent; config: Cfg; isBusy?: boolean }) => {
|
|
272
255
|
const [pupilIndex, setPupilIndex] = createSignal(0)
|
|
273
256
|
const [blinkFrame, setBlinkFrame] = createSignal(0)
|
|
@@ -328,17 +311,17 @@ const SidebarMustachi = (props: { theme: TuiThemeCurrent; config: Cfg; isBusy?:
|
|
|
328
311
|
return
|
|
329
312
|
}
|
|
330
313
|
|
|
331
|
-
// Grow tongue progressively when entering busy state
|
|
314
|
+
// Grow tongue progressively when entering busy state (2 frames: hidden -> visible)
|
|
332
315
|
let currentFrame = 0
|
|
333
316
|
let tongueTimeoutId: NodeJS.Timeout | undefined
|
|
334
317
|
const growTongue = () => {
|
|
335
318
|
if (currentFrame < tongueFrames.length - 1) {
|
|
336
319
|
currentFrame++
|
|
337
320
|
setTongueFrame(currentFrame)
|
|
338
|
-
tongueTimeoutId = setTimeout(growTongue, 200)
|
|
339
321
|
}
|
|
340
322
|
}
|
|
341
|
-
|
|
323
|
+
// Show tongue immediately when busy
|
|
324
|
+
tongueTimeoutId = setTimeout(growTongue, 200)
|
|
342
325
|
|
|
343
326
|
// Rotate busy phrases
|
|
344
327
|
let phraseIdx = 0
|
|
@@ -371,12 +354,12 @@ const SidebarMustachi = (props: { theme: TuiThemeCurrent; config: Cfg; isBusy?:
|
|
|
371
354
|
: blinkFrames[blinkFrame()].left
|
|
372
355
|
}
|
|
373
356
|
|
|
374
|
-
// Add eyes with pupil position (modify line
|
|
357
|
+
// Add eyes with pupil position (modify line 2 for left eye pupil - index 2 in 5-line array)
|
|
375
358
|
eyeBase.forEach((line, idx) => {
|
|
376
|
-
if (idx ===
|
|
377
|
-
// Replace pupil in left eye (
|
|
359
|
+
if (idx === 2 && !props.isBusy && pupilIndex() >= 0) {
|
|
360
|
+
// Replace pupil in left eye (positions 2-12 of the line for the 25-char compact design)
|
|
378
361
|
const pupil = leftPupilPositions[pupilIndex()]
|
|
379
|
-
const modifiedLine = line.substring(0,
|
|
362
|
+
const modifiedLine = line.substring(0, 2) + pupil + line.substring(13)
|
|
380
363
|
lines.push(modifiedLine)
|
|
381
364
|
} else {
|
|
382
365
|
lines.push(line)
|
|
@@ -386,10 +369,10 @@ const SidebarMustachi = (props: { theme: TuiThemeCurrent; config: Cfg; isBusy?:
|
|
|
386
369
|
// Add mustache section
|
|
387
370
|
mustachiMustacheSection.forEach(line => lines.push(line))
|
|
388
371
|
|
|
389
|
-
// Add tongue if busy (progressive frames)
|
|
372
|
+
// Add tongue if busy (progressive frames) - mark as tongue for coloring
|
|
390
373
|
if (props.isBusy && tongueFrame() > 0) {
|
|
391
374
|
const tongueLines = tongueFrames[tongueFrame()]
|
|
392
|
-
tongueLines.forEach(line => lines.push(line))
|
|
375
|
+
tongueLines.forEach(line => lines.push(`TONGUE:${line}`))
|
|
393
376
|
}
|
|
394
377
|
|
|
395
378
|
return lines
|
|
@@ -397,22 +380,33 @@ const SidebarMustachi = (props: { theme: TuiThemeCurrent; config: Cfg; isBusy?:
|
|
|
397
380
|
|
|
398
381
|
const faceLines = buildFace()
|
|
399
382
|
|
|
400
|
-
|
|
401
|
-
const
|
|
402
|
-
const
|
|
383
|
+
// Grayscale palette for TUI clarity
|
|
384
|
+
const lightGray = "#C0C0C0" // Light gray for highlights
|
|
385
|
+
const midGray = "#808080" // Mid gray for main body
|
|
386
|
+
const darkGray = "#505050" // Dark gray for shadows
|
|
387
|
+
const tongueColor = "#FF4466" // Pink/Red for tongue
|
|
403
388
|
|
|
404
389
|
return (
|
|
405
390
|
<box flexDirection="column" alignItems="center">
|
|
406
|
-
{/* Full Mustachi face with
|
|
391
|
+
{/* Full Mustachi face with grayscale gradient + pink tongue */}
|
|
407
392
|
{faceLines.map((line, idx) => {
|
|
393
|
+
// Check if this is a tongue line
|
|
394
|
+
const isTongue = line.startsWith("TONGUE:")
|
|
395
|
+
const displayLine = isTongue ? line.substring(7) : line
|
|
396
|
+
|
|
397
|
+
if (isTongue) {
|
|
398
|
+
return <text fg={tongueColor}>{displayLine}</text>
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Apply grayscale gradient to eyes and mustache
|
|
408
402
|
const totalLines = faceLines.length
|
|
409
|
-
let color =
|
|
403
|
+
let color = midGray
|
|
410
404
|
if (idx < totalLines / 3) {
|
|
411
|
-
color =
|
|
405
|
+
color = lightGray // Top highlight
|
|
412
406
|
} else if (idx >= (2 * totalLines) / 3) {
|
|
413
|
-
color =
|
|
407
|
+
color = darkGray // Bottom shadow
|
|
414
408
|
}
|
|
415
|
-
return <text fg={color}>{
|
|
409
|
+
return <text fg={color}>{displayLine}</text>
|
|
416
410
|
})}
|
|
417
411
|
|
|
418
412
|
{/* Busy phrase if loading */}
|