plugin-gentleman 1.0.9 → 1.1.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.
- package/README.md +11 -11
- package/ascii-frames.ts +21 -21
- package/components.tsx +8 -1
- package/package.json +1 -1
- package/tui.tsx +23 -8
package/README.md
CHANGED
|
@@ -98,7 +98,7 @@ The ASCII representation features:
|
|
|
98
98
|
- **Eyes** that blink and look in 8 directions (center, up, down, left, right, and 4 diagonals) *(sidebar only)*
|
|
99
99
|
- **Mustache** rendered in grayscale gradient on home screen, semantic zone colors in sidebar
|
|
100
100
|
- **Tongue** that appears during busy states or periodic expressive cycles *(sidebar only)*
|
|
101
|
-
- **Motivational phrases** in Rioplatense Spanish style —
|
|
101
|
+
- **Motivational phrases** in Rioplatense Spanish style — single random phrase rotating every 3s *(sidebar only)*
|
|
102
102
|
|
|
103
103
|
**Example phrases during busy states:**
|
|
104
104
|
- *"Ponete las pilas, hermano..."*
|
|
@@ -124,9 +124,9 @@ The ASCII representation features:
|
|
|
124
124
|
- Returns to center frequently for natural feel
|
|
125
125
|
|
|
126
126
|
**Busy/Expressive State** *(when `animations: true`)*
|
|
127
|
-
- Tongue appears
|
|
127
|
+
- Tongue appears when OpenCode is processing
|
|
128
128
|
- Eyes squint during expressive state
|
|
129
|
-
-
|
|
129
|
+
- Single motivational phrase rotating every 3 seconds (36+ phrase library)
|
|
130
130
|
- Active during detected busy states OR periodic expressive cycles
|
|
131
131
|
|
|
132
132
|
**Expressive Cycle Fallback** *(when `animations: true`)*
|
|
@@ -388,7 +388,7 @@ The plugin is organized into focused modules for easy customization:
|
|
|
388
388
|
**Adding new content:**
|
|
389
389
|
|
|
390
390
|
- **Motivational phrases:** Edit `phrases.ts` — add new phrases to the `busyPhrases` array (currently 36+ phrases)
|
|
391
|
-
- **ASCII art frames:** Edit `ascii-frames.ts` — modify eye positions (9 variants), blink frames (3 stages), mustache designs, or tongue
|
|
391
|
+
- **ASCII art frames:** Edit `ascii-frames.ts` — modify eye positions (9 variants), blink frames (3 stages), mustache designs, or tongue states (binary on/off)
|
|
392
392
|
- **UI logic:** Edit `components.tsx` — adjust animation timings, add new effects, or tweak component layout
|
|
393
393
|
- **Configuration:** Edit `config.ts` — add new config options with type-safe defaults
|
|
394
394
|
- **Detection logic:** Edit `detection.ts` — add new OS detection patterns or provider mappings
|
|
@@ -404,17 +404,17 @@ The plugin is organized into focused modules for easy customization:
|
|
|
404
404
|
**Animation timing customization:**
|
|
405
405
|
|
|
406
406
|
All animation intervals are in `components.tsx`:
|
|
407
|
-
- **Look-around interval:**
|
|
408
|
-
- **Blink interval:**
|
|
409
|
-
- **Blink frame timing:**
|
|
410
|
-
- **Phrase rotation:**
|
|
411
|
-
- **Expressive cycle timing:**
|
|
412
|
-
- **Expressive cycle duration:**
|
|
407
|
+
- **Look-around interval:** Currently 3000ms (3s)
|
|
408
|
+
- **Blink interval:** Currently 2000ms with 35% chance (~5-6s average)
|
|
409
|
+
- **Blink frame timing:** Currently 80-100ms per frame progression
|
|
410
|
+
- **Phrase rotation:** Currently 3000ms (3s) during expressive state
|
|
411
|
+
- **Expressive cycle timing:** First cycle at 30-45s, then every 45-60s
|
|
412
|
+
- **Expressive cycle duration:** Currently 8000ms (8s)
|
|
413
413
|
|
|
414
414
|
**Color customization:**
|
|
415
415
|
|
|
416
416
|
- **Zone colors (sidebar):** Edit `zoneColors` object in `ascii-frames.ts` (monocle, eyes, mustache, tongue)
|
|
417
|
-
- **Home grayscale gradient:** Edit `HomeLogo` component in `components.tsx` (
|
|
417
|
+
- **Home grayscale gradient:** Edit `HomeLogo` component in `components.tsx` (top/middle/bottom color values)
|
|
418
418
|
- **Theme colors:** Edit `gentleman.json` for the full color palette
|
|
419
419
|
|
|
420
420
|
---
|
package/ascii-frames.ts
CHANGED
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
export const eyeNeutralCenter = [
|
|
10
10
|
" █████ █████ ", // 27 chars
|
|
11
11
|
" ██░░░░░██ ██░░░░░██ ", // 27 chars
|
|
12
|
-
"
|
|
13
|
-
"
|
|
12
|
+
" ██░░ ░░██ ██░░░░░░░██ ", // 27 chars - hollow pupil center
|
|
13
|
+
" ██░░ ░░██ ██░░░░░░░██ ", // 27 chars - hollow pupil center
|
|
14
14
|
"██ ██░░░░░██ ██░░░░░██ ██", // 27 chars
|
|
15
15
|
]
|
|
16
16
|
|
|
17
17
|
export const eyeNeutralUp = [
|
|
18
18
|
" █████ █████ ", // 27 chars
|
|
19
|
-
"
|
|
20
|
-
"
|
|
19
|
+
" ██░░ ██ ██░░░░░██ ", // 27 chars - hollow pupil up
|
|
20
|
+
" ██░░░ ░░██ ██░░░░░░░██ ", // 27 chars - hollow pupil up
|
|
21
21
|
" ██░░░░░░░██ ██░░░░░░░██ ", // 27 chars
|
|
22
22
|
"██ ██░░░░░██ ██░░░░░██ ██", // 27 chars
|
|
23
23
|
]
|
|
@@ -26,38 +26,38 @@ export const eyeNeutralDown = [
|
|
|
26
26
|
" █████ █████ ", // 27 chars
|
|
27
27
|
" ██░░░░░██ ██░░░░░██ ", // 27 chars
|
|
28
28
|
" ██░░░░░░░██ ██░░░░░░░██ ", // 27 chars
|
|
29
|
-
"
|
|
30
|
-
"██
|
|
29
|
+
" ██░░ ░░██ ██░░░░░░░██ ", // 27 chars - hollow pupil down
|
|
30
|
+
"██ ██░░ ░██ ██░░░░░██ ██", // 27 chars - hollow pupil down
|
|
31
31
|
]
|
|
32
32
|
|
|
33
33
|
export const eyeNeutralLeft = [
|
|
34
34
|
" █████ █████ ", // 27 chars
|
|
35
35
|
" ██░░░░░██ ██░░░░░██ ", // 27 chars
|
|
36
|
-
"
|
|
37
|
-
"
|
|
36
|
+
" ██ ░░░░██ ██░░░░░░░██ ", // 27 chars - hollow pupil left
|
|
37
|
+
" ██ ░░░░██ ██░░░░░░░██ ", // 27 chars - hollow pupil left
|
|
38
38
|
"██ ██░░░░░██ ██░░░░░██ ██", // 27 chars
|
|
39
39
|
]
|
|
40
40
|
|
|
41
41
|
export const eyeNeutralRight = [
|
|
42
42
|
" █████ █████ ", // 27 chars
|
|
43
43
|
" ██░░░░░██ ██░░░░░██ ", // 27 chars
|
|
44
|
-
"
|
|
45
|
-
"
|
|
44
|
+
" ██░░░░░ ██ ██░░░░░░░██ ", // 27 chars - hollow pupil right
|
|
45
|
+
" ██░░░░░ ██ ██░░░░░░░██ ", // 27 chars - hollow pupil right
|
|
46
46
|
"██ ██░░░░░██ ██░░░░░██ ██", // 27 chars
|
|
47
47
|
]
|
|
48
48
|
|
|
49
49
|
export const eyeNeutralUpLeft = [
|
|
50
50
|
" █████ █████ ", // 27 chars
|
|
51
|
-
"
|
|
52
|
-
"
|
|
51
|
+
" ██ ░░██ ██░░░░░██ ", // 27 chars - hollow pupil up-left
|
|
52
|
+
" ██ ░░░░░██ ██░░░░░░░██ ", // 27 chars - hollow pupil up-left
|
|
53
53
|
" ██░░░░░░░██ ██░░░░░░░██ ", // 27 chars
|
|
54
54
|
"██ ██░░░░░██ ██░░░░░██ ██", // 27 chars
|
|
55
55
|
]
|
|
56
56
|
|
|
57
57
|
export const eyeNeutralUpRight = [
|
|
58
58
|
" █████ █████ ", // 27 chars
|
|
59
|
-
"
|
|
60
|
-
"
|
|
59
|
+
" ██░░░░ ██ ██░░░░░██ ", // 27 chars - hollow pupil up-right
|
|
60
|
+
" ██░░░░░ ██ ██░░░░░░░██ ", // 27 chars - hollow pupil up-right
|
|
61
61
|
" ██░░░░░░░██ ██░░░░░░░██ ", // 27 chars
|
|
62
62
|
"██ ██░░░░░██ ██░░░░░██ ██", // 27 chars
|
|
63
63
|
]
|
|
@@ -66,23 +66,23 @@ export const eyeNeutralDownLeft = [
|
|
|
66
66
|
" █████ █████ ", // 27 chars
|
|
67
67
|
" ██░░░░░██ ██░░░░░██ ", // 27 chars
|
|
68
68
|
" ██░░░░░░░██ ██░░░░░░░██ ", // 27 chars
|
|
69
|
-
"
|
|
70
|
-
"██
|
|
69
|
+
" ██ ░░░░░██ ██░░░░░░░██ ", // 27 chars - hollow pupil down-left
|
|
70
|
+
"██ ██ ░░░░██ ██░░░░░██ ██", // 27 chars - hollow pupil down-left
|
|
71
71
|
]
|
|
72
72
|
|
|
73
73
|
export const eyeNeutralDownRight = [
|
|
74
74
|
" █████ █████ ", // 27 chars
|
|
75
75
|
" ██░░░░░██ ██░░░░░██ ", // 27 chars
|
|
76
76
|
" ██░░░░░░░██ ██░░░░░░░██ ", // 27 chars
|
|
77
|
-
"
|
|
78
|
-
"██
|
|
77
|
+
" ██░░░░░ ██ ██░░░░░░░██ ", // 27 chars - hollow pupil down-right
|
|
78
|
+
"██ ██░░░░ ██ ██░░░░░██ ██", // 27 chars - hollow pupil down-right
|
|
79
79
|
]
|
|
80
80
|
|
|
81
81
|
// Squinted eyes version for busy/expressive state
|
|
82
82
|
export const eyeSquinted = [
|
|
83
83
|
" █████ █████ ", // 27 chars
|
|
84
84
|
" ██░░░░░██ ██░░░░░██ ", // 27 chars
|
|
85
|
-
"
|
|
85
|
+
" ██░░ ░░██ ██░░░░░░░██ ", // 27 chars
|
|
86
86
|
" █████████ █████████ ", // 27 chars
|
|
87
87
|
"██ █████ █████ ██", // 27 chars
|
|
88
88
|
]
|
|
@@ -91,8 +91,8 @@ export const eyeSquinted = [
|
|
|
91
91
|
export const eyeBlinkHalf = [
|
|
92
92
|
" █████ █████ ", // 27 chars - monocle border top unchanged
|
|
93
93
|
" █████████ ██████████ ", // 27 chars - upper eyelid descends halfway
|
|
94
|
-
"
|
|
95
|
-
"
|
|
94
|
+
" ██░░ ░░██ ██░░░░░░░██ ", // 27 chars - pupils still visible
|
|
95
|
+
" ██░░ ░░██ ██░░░░░░░██ ", // 27 chars - pupils still visible
|
|
96
96
|
"██ ██░░░░░██ ██░░░░░██ ██", // 27 chars - bottom unchanged
|
|
97
97
|
]
|
|
98
98
|
|
package/components.tsx
CHANGED
|
@@ -161,11 +161,13 @@ export const SidebarMustachi = (props: { theme: TuiThemeCurrent; config: Cfg; is
|
|
|
161
161
|
createEffect(() => {
|
|
162
162
|
if (!props.config.animations || props.isBusy) return
|
|
163
163
|
|
|
164
|
+
let cycleEndTimeout: NodeJS.Timeout | undefined
|
|
165
|
+
|
|
164
166
|
const triggerExpressiveCycle = () => {
|
|
165
167
|
setExpressiveCycle(true)
|
|
166
168
|
|
|
167
169
|
// End expressive cycle after 8 seconds
|
|
168
|
-
setTimeout(() => {
|
|
170
|
+
cycleEndTimeout = setTimeout(() => {
|
|
169
171
|
setExpressiveCycle(false)
|
|
170
172
|
}, 8000)
|
|
171
173
|
}
|
|
@@ -181,6 +183,11 @@ export const SidebarMustachi = (props: { theme: TuiThemeCurrent; config: Cfg; is
|
|
|
181
183
|
onCleanup(() => {
|
|
182
184
|
clearTimeout(firstTimeout)
|
|
183
185
|
clearInterval(interval)
|
|
186
|
+
if (cycleEndTimeout !== undefined) {
|
|
187
|
+
clearTimeout(cycleEndTimeout)
|
|
188
|
+
}
|
|
189
|
+
// Explicitly reset expressive cycle state to prevent sticking
|
|
190
|
+
setExpressiveCycle(false)
|
|
184
191
|
})
|
|
185
192
|
})
|
|
186
193
|
|
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.
|
|
4
|
+
"version": "1.1.1",
|
|
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
|
@@ -19,20 +19,35 @@ const tui: TuiPlugin = async (api, options) => {
|
|
|
19
19
|
const [value] = createSignal(boot)
|
|
20
20
|
const [isBusy, setIsBusy] = createSignal(false)
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
api.theme.
|
|
22
|
+
// Theme setup - wrapped in try-catch to avoid breaking plugin bootstrap
|
|
23
|
+
try {
|
|
24
|
+
await api.theme.install("./gentleman.json")
|
|
25
|
+
if (value().set_theme) {
|
|
26
|
+
api.theme.set(value().theme)
|
|
27
|
+
}
|
|
28
|
+
} catch (error) {
|
|
29
|
+
// Theme installation failed - log but continue with slot registration
|
|
30
|
+
// Plugin will still render with default theme
|
|
31
|
+
console.error("[plugin-gentleman] Theme setup failed:", error)
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
// Detect busy state if API exposes it
|
|
28
|
-
//
|
|
35
|
+
// Note: Best-effort detection - OpenCode TUI may not expose session.running
|
|
36
|
+
// If unavailable, isBusy remains false and expressive cycle fallback handles animations
|
|
29
37
|
createEffect(() => {
|
|
30
38
|
try {
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
// Attempt to reactively track running state if API supports it
|
|
40
|
+
// This may or may not work depending on OpenCode version
|
|
41
|
+
if (api.state?.session?.running !== undefined) {
|
|
42
|
+
setIsBusy(!!api.state.session.running)
|
|
43
|
+
} else {
|
|
44
|
+
// API doesn't expose running state - degrade gracefully
|
|
45
|
+
// Expressive cycle fallback in components.tsx will demonstrate animations
|
|
46
|
+
setIsBusy(false)
|
|
47
|
+
}
|
|
34
48
|
} catch {
|
|
35
|
-
// If API doesn't expose this
|
|
49
|
+
// If API doesn't expose this at all, stay in idle state
|
|
50
|
+
// Periodic expressive cycles ensure animations are still visible
|
|
36
51
|
setIsBusy(false)
|
|
37
52
|
}
|
|
38
53
|
})
|