plugin-gentleman 1.0.2 → 1.0.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/tui.tsx +81 -96
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.2",
4
+ "version": "1.0.4",
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 - full version for sidebar
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 (shared by all states)
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
- [" ███████"], // small tongue
58
- [" ███████", " █████"], // medium tongue
59
- [" ███████", " █████", " ███"], // full tongue
40
+ [" ███", " █"], // tongue out
60
41
  ]
61
42
 
62
- // Mustache-only ASCII art for home logo (prominent and simple)
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
- "████████", // center (line 3 of eyes)
87
- "██████ ", // looking left
88
- " ██████", // looking right
89
- "████████", // center again
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,32 @@ 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
- const topColor = props.theme.accent || "#E0C15A"
241
- const midColor = props.theme.primary || "#7FB4CA"
242
- const bottomColor = props.theme.error || "#CB7C94"
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-only with 3-tone gradient */}
221
+ {/* Mustache with grayscale gradient for depth */}
247
222
  {mustachiMustacheOnly.map((line, idx) => {
248
223
  const totalLines = mustachiMustacheOnly.length
249
- let color = midColor
224
+ let color = midGray
250
225
  if (idx < totalLines / 3) {
251
- color = topColor
226
+ color = lightGray // Top highlight
252
227
  } else if (idx >= (2 * totalLines) / 3) {
253
- color = bottomColor
228
+ color = darkGray // Bottom shadow
254
229
  }
255
- return <text fg={color}>{line}</text>
230
+ return <text fg={color}>{line.padEnd(61, " ")}</text>
256
231
  })}
257
232
 
258
233
  {/* OpenCode branding */}
259
- <box flexDirection="row" gap={0}>
260
- <text fg={props.theme.textMuted} dimColor={true}>╭</text>
261
- <text fg={props.theme.primary} dimColor={false}> OpenCode </text>
262
- <text fg={props.theme.textMuted} dimColor={true}>╮</text>
234
+ <box flexDirection="row" gap={0} marginTop={1}>
235
+ <text fg={props.theme.textMuted} dimColor={true}>╭ </text>
236
+ <text fg={props.theme.primary} bold={true}> O p e n C o d e </text>
237
+ <text fg={props.theme.textMuted} dimColor={true}> ╮</text>
263
238
  </box>
264
239
 
265
240
  <text> </text>
@@ -267,7 +242,7 @@ const HomeLogo = (props: { theme: TuiThemeCurrent }) => {
267
242
  )
268
243
  }
269
244
 
270
- // Sidebar: Full Mustachi face with progressive animations
245
+ // Sidebar: Full Mustachi face with progressive animations (grayscale for clarity)
271
246
  const SidebarMustachi = (props: { theme: TuiThemeCurrent; config: Cfg; isBusy?: boolean }) => {
272
247
  const [pupilIndex, setPupilIndex] = createSignal(0)
273
248
  const [blinkFrame, setBlinkFrame] = createSignal(0)
@@ -328,17 +303,17 @@ const SidebarMustachi = (props: { theme: TuiThemeCurrent; config: Cfg; isBusy?:
328
303
  return
329
304
  }
330
305
 
331
- // Grow tongue progressively when entering busy state
306
+ // Grow tongue progressively when entering busy state (2 frames: hidden -> visible)
332
307
  let currentFrame = 0
333
308
  let tongueTimeoutId: NodeJS.Timeout | undefined
334
309
  const growTongue = () => {
335
310
  if (currentFrame < tongueFrames.length - 1) {
336
311
  currentFrame++
337
312
  setTongueFrame(currentFrame)
338
- tongueTimeoutId = setTimeout(growTongue, 200)
339
313
  }
340
314
  }
341
- growTongue()
315
+ // Show tongue immediately when busy
316
+ tongueTimeoutId = setTimeout(growTongue, 200)
342
317
 
343
318
  // Rotate busy phrases
344
319
  let phraseIdx = 0
@@ -371,12 +346,12 @@ const SidebarMustachi = (props: { theme: TuiThemeCurrent; config: Cfg; isBusy?:
371
346
  : blinkFrames[blinkFrame()].left
372
347
  }
373
348
 
374
- // Add eyes with pupil position (modify line 3 for left eye pupil)
349
+ // Add eyes with pupil position (modify line 2 for left eye pupil - index 2 in 5-line array)
375
350
  eyeBase.forEach((line, idx) => {
376
- if (idx === 3 && !props.isBusy && pupilIndex() >= 0) {
377
- // Replace pupil in left eye (center of line 3)
351
+ if (idx === 2 && !props.isBusy && pupilIndex() >= 0) {
352
+ // Replace pupil in left eye (positions 2-12 of the line for the 25-char compact design)
378
353
  const pupil = leftPupilPositions[pupilIndex()]
379
- const modifiedLine = line.substring(0, 14) + pupil + line.substring(22)
354
+ const modifiedLine = line.substring(0, 2) + pupil + line.substring(13)
380
355
  lines.push(modifiedLine)
381
356
  } else {
382
357
  lines.push(line)
@@ -386,33 +361,43 @@ const SidebarMustachi = (props: { theme: TuiThemeCurrent; config: Cfg; isBusy?:
386
361
  // Add mustache section
387
362
  mustachiMustacheSection.forEach(line => lines.push(line))
388
363
 
389
- // Add tongue if busy (progressive frames)
364
+ // Add tongue if busy (progressive frames) - mark as tongue for coloring
390
365
  if (props.isBusy && tongueFrame() > 0) {
391
366
  const tongueLines = tongueFrames[tongueFrame()]
392
- tongueLines.forEach(line => lines.push(line))
367
+ tongueLines.forEach(line => lines.push(`TONGUE:${line}`))
393
368
  }
394
369
 
395
370
  return lines
396
371
  }
397
372
 
398
- const faceLines = buildFace()
399
-
400
- const topColor = props.theme.accent || "#E0C15A"
401
- const midColor = props.theme.primary || "#7FB4CA"
402
- const bottomColor = props.theme.error || "#CB7C94"
373
+ // Grayscale palette for TUI clarity
374
+ const lightGray = "#C0C0C0" // Light gray for highlights
375
+ const midGray = "#808080" // Mid gray for main body
376
+ const darkGray = "#505050" // Dark gray for shadows
377
+ const tongueColor = "#FF4466" // Pink/Red for tongue
403
378
 
404
379
  return (
405
380
  <box flexDirection="column" alignItems="center">
406
- {/* Full Mustachi face with 3-tone gradient */}
407
- {faceLines.map((line, idx) => {
408
- const totalLines = faceLines.length
409
- let color = midColor
381
+ {/* Full Mustachi face with grayscale gradient + pink tongue */}
382
+ {buildFace().map((line, idx, arr) => {
383
+ // Check if this is a tongue line
384
+ const isTongue = line.startsWith("TONGUE:")
385
+ const displayLine = isTongue ? line.substring(7) : line
386
+ const paddedLine = displayLine.padEnd(25, " ")
387
+
388
+ if (isTongue) {
389
+ return <text fg={tongueColor}>{paddedLine}</text>
390
+ }
391
+
392
+ // Apply grayscale gradient to eyes and mustache
393
+ const totalLines = arr.length
394
+ let color = midGray
410
395
  if (idx < totalLines / 3) {
411
- color = topColor
396
+ color = lightGray // Top highlight
412
397
  } else if (idx >= (2 * totalLines) / 3) {
413
- color = bottomColor
398
+ color = darkGray // Bottom shadow
414
399
  }
415
- return <text fg={color}>{line}</text>
400
+ return <text fg={color}>{paddedLine}</text>
416
401
  })}
417
402
 
418
403
  {/* Busy phrase if loading */}