@kernel.chat/kbot 3.94.0 → 3.97.0

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.
@@ -151,7 +151,7 @@ function drawHead(ctx, s, ox, oy, eyeColor, mood, frame, headShiftX) {
151
151
  // Eye glow background — brighter white-green for alive look, dimmed if dreaming
152
152
  const eyeC = mood === 'dreaming'
153
153
  ? dimColor(eyeColor.startsWith('rgb') ? '#4a6670' : eyeColor, 0.5)
154
- : '#80ffb0'; // bright cyan-green that contrasts against the green head
154
+ : '#b0ffe0'; // HACK 3: even brighter cyan-white for maximum pop
155
155
  px(ctx, hx + 2, eyeY, 4, eyeH, eyeC, s, ox, oy);
156
156
  px(ctx, hx + 8, eyeY, 4, eyeH, eyeC, s, ox, oy);
157
157
  // Specular highlights on eyes — 2px L-shape catch-light for glassy/alive look (technique 8)
@@ -895,6 +895,440 @@ function drawWalkingLegs(ctx, s, ox, oy, bodyShiftY, walkPhase) {
895
895
  px(ctx, 21 + rightLegOffset, footY, 1, 2, PAL.rimLight, s, ox, oy);
896
896
  px(ctx, 17 + rightLegOffset, footY + 2, 4, 1, PAL.jetOrange, s, ox, oy);
897
897
  }
898
+ // ─── Gorilla Character ────────────────────────────────────────
899
+ const GORILLA = {
900
+ furDark: '#8B6914', // dark brown
901
+ furMain: '#C4943D', // main tan/brown
902
+ furLight: '#DEB860', // light tan highlights
903
+ furChest: '#E8D5A0', // pale chest/face
904
+ skinDark: '#7A5B2E', // darker skin (face creases)
905
+ eyeWhite: '#F0F0E0', // eye whites
906
+ eyePupil: '#1a1a1a', // dark pupils
907
+ mouth: '#3D2B1A', // dark mouth
908
+ fang: '#F0F0E0', // white fangs
909
+ capRed: '#CC2222', // red cap
910
+ capWhite: '#F0F0F0', // white cap panel
911
+ capBrim: '#999999', // brim underside
912
+ claws: '#D0D0D0', // light gray claws
913
+ outline: '#2A1F0A', // dark brown outline
914
+ nose: '#5A3D1E', // nose color
915
+ };
916
+ let _gorillaPrevMood = '';
917
+ let _gorillaSettleFrames = 0;
918
+ /**
919
+ * Draw a stocky gorilla/monkey pixel art character (32x32 grid).
920
+ * Drop-in replacement for drawRobot() with the same signature.
921
+ *
922
+ * @param ctx - Canvas 2D rendering context
923
+ * @param x - Top-left X position in canvas pixels
924
+ * @param y - Top-left Y position in canvas pixels
925
+ * @param scale - Pixel scale multiplier (4-10 recommended)
926
+ * @param mood - Current mood: idle, talking, thinking, excited, dancing, walking
927
+ * @param frame - Animation frame counter (incrementing integer)
928
+ * @param moodColor - Optional RGB override for mood accent color
929
+ */
930
+ export function drawGorilla(ctx, x, y, scale, mood, frame, moodColor) {
931
+ const s = scale;
932
+ const G = GORILLA;
933
+ // Settle animation on mood change
934
+ if (mood !== _gorillaPrevMood) {
935
+ _gorillaSettleFrames = 3;
936
+ _gorillaPrevMood = mood;
937
+ }
938
+ let settleShift = 0;
939
+ if (_gorillaSettleFrames > 0) {
940
+ settleShift = _gorillaSettleFrames === 3 ? -1 : _gorillaSettleFrames === 2 ? 1 : 0;
941
+ _gorillaSettleFrames--;
942
+ }
943
+ // ── Animation offsets ──
944
+ let bodyY = settleShift;
945
+ let bodyX = 0;
946
+ let headTilt = 0;
947
+ let mouthOpen = 0; // 0=closed, 1=half, 2=open, 3=wide
948
+ let leftArmFwd = 0; // forward offset for walking
949
+ let rightArmFwd = 0;
950
+ let eyeState = 'narrow'; // default grumpy
951
+ let capTilt = 0;
952
+ let questionMark = false;
953
+ // Breathing (idle)
954
+ const breathFrame = frame % 12;
955
+ let breathShift = 0;
956
+ if (mood === 'idle') {
957
+ if (breathFrame >= 1 && breathFrame <= 3)
958
+ breathShift = -1; // rise
959
+ if (breathFrame >= 4 && breathFrame <= 5) {
960
+ breathShift = 0;
961
+ bodyY += 1;
962
+ }
963
+ // Blink every ~24 frames
964
+ if (frame % 24 === 23)
965
+ eyeState = 'blink';
966
+ else if (frame % 24 === 22)
967
+ eyeState = 'blink';
968
+ }
969
+ if (mood === 'talking') {
970
+ // Mouth animation cycles
971
+ mouthOpen = frame % 4; // 0=open, 1=half, 2=wide, 3=closed
972
+ eyeState = 'narrow';
973
+ }
974
+ else if (mood === 'walking') {
975
+ // Walking: arms alternate, body bobs
976
+ const wf = frame % 4;
977
+ if (wf === 0) {
978
+ leftArmFwd = -2;
979
+ rightArmFwd = 2;
980
+ bodyX = -1;
981
+ }
982
+ else if (wf === 1) {
983
+ leftArmFwd = 0;
984
+ rightArmFwd = 0;
985
+ }
986
+ else if (wf === 2) {
987
+ leftArmFwd = 2;
988
+ rightArmFwd = -2;
989
+ bodyX = 1;
990
+ }
991
+ else {
992
+ leftArmFwd = 0;
993
+ rightArmFwd = 0;
994
+ }
995
+ bodyY += (wf % 2 === 0) ? -1 : 0;
996
+ }
997
+ else if (mood === 'excited') {
998
+ const ef = frame % 4;
999
+ if (ef === 0) {
1000
+ bodyY -= 2;
1001
+ }
1002
+ else if (ef === 2) {
1003
+ bodyY -= 3;
1004
+ mouthOpen = 2;
1005
+ eyeState = 'wide';
1006
+ }
1007
+ else {
1008
+ eyeState = 'narrow';
1009
+ }
1010
+ }
1011
+ else if (mood === 'thinking') {
1012
+ const tf = frame % 3;
1013
+ if (tf === 0) {
1014
+ headTilt = 1;
1015
+ }
1016
+ else if (tf === 1) {
1017
+ eyeState = 'up';
1018
+ }
1019
+ else {
1020
+ questionMark = true;
1021
+ }
1022
+ }
1023
+ else if (mood === 'dancing') {
1024
+ const df = frame % 4;
1025
+ if (df === 0) {
1026
+ bodyX = -2;
1027
+ bodyY -= 1;
1028
+ }
1029
+ else if (df === 1) {
1030
+ bodyX = 0;
1031
+ }
1032
+ else if (df === 2) {
1033
+ bodyX = 2;
1034
+ bodyY -= 1;
1035
+ }
1036
+ else {
1037
+ bodyX = 0;
1038
+ capTilt = 1;
1039
+ }
1040
+ eyeState = 'narrow';
1041
+ mouthOpen = (df % 2 === 0) ? 1 : 0;
1042
+ }
1043
+ bodyY += breathShift;
1044
+ const ox = x + bodyX * s;
1045
+ const oy = y + bodyY * s;
1046
+ // ── Drop shadow ──
1047
+ ctx.save();
1048
+ ctx.fillStyle = 'rgba(30, 20, 5, 0.3)';
1049
+ ctx.beginPath();
1050
+ ctx.ellipse(ox + 16 * s, oy + 31 * s, 12 * s, 3 * s, 0, 0, Math.PI * 2);
1051
+ ctx.fill();
1052
+ ctx.restore();
1053
+ // ── Tail (draw first, behind body) ──
1054
+ // Curled tail on the right side, rows 14-20
1055
+ px(ctx, 27, 14, 2, 1, G.furMain, s, ox, oy);
1056
+ px(ctx, 28, 15, 2, 1, G.furMain, s, ox, oy);
1057
+ px(ctx, 29, 16, 2, 1, G.furDark, s, ox, oy);
1058
+ px(ctx, 29, 17, 1, 1, G.furDark, s, ox, oy);
1059
+ px(ctx, 28, 18, 1, 1, G.furMain, s, ox, oy);
1060
+ px(ctx, 27, 19, 2, 1, G.furMain, s, ox, oy);
1061
+ // Curl tip
1062
+ px(ctx, 26, 18, 1, 1, G.furLight, s, ox, oy);
1063
+ // Outline
1064
+ px(ctx, 27, 13, 2, 1, G.outline, s, ox, oy);
1065
+ px(ctx, 29, 14, 1, 1, G.outline, s, ox, oy);
1066
+ px(ctx, 30, 15, 1, 2, G.outline, s, ox, oy);
1067
+ px(ctx, 31, 16, 1, 1, G.outline, s, ox, oy);
1068
+ px(ctx, 30, 17, 1, 1, G.outline, s, ox, oy);
1069
+ px(ctx, 30, 18, 1, 1, G.outline, s, ox, oy);
1070
+ px(ctx, 29, 19, 1, 1, G.outline, s, ox, oy);
1071
+ px(ctx, 27, 20, 2, 1, G.outline, s, ox, oy);
1072
+ px(ctx, 25, 18, 1, 1, G.outline, s, ox, oy);
1073
+ // ── Back legs (behind body) ──
1074
+ // Left back leg (rows 22-28)
1075
+ px(ctx, 17, 24, 5, 5, G.furDark, s, ox, oy);
1076
+ px(ctx, 18, 24, 3, 4, G.furMain, s, ox, oy);
1077
+ px(ctx, 17, 29, 6, 2, G.furDark, s, ox, oy); // foot
1078
+ px(ctx, 18, 29, 4, 1, G.furMain, s, ox, oy);
1079
+ // Claws on back foot
1080
+ px(ctx, 17, 31, 1, 1, G.claws, s, ox, oy);
1081
+ px(ctx, 19, 31, 1, 1, G.claws, s, ox, oy);
1082
+ px(ctx, 21, 31, 1, 1, G.claws, s, ox, oy);
1083
+ // Right back leg
1084
+ px(ctx, 22, 24, 5, 5, G.furDark, s, ox, oy);
1085
+ px(ctx, 23, 24, 3, 4, G.furMain, s, ox, oy);
1086
+ px(ctx, 22, 29, 6, 2, G.furDark, s, ox, oy); // foot
1087
+ px(ctx, 23, 29, 4, 1, G.furMain, s, ox, oy);
1088
+ // Claws
1089
+ px(ctx, 22, 31, 1, 1, G.claws, s, ox, oy);
1090
+ px(ctx, 24, 31, 1, 1, G.claws, s, ox, oy);
1091
+ px(ctx, 26, 31, 1, 1, G.claws, s, ox, oy);
1092
+ // ── Body (rows 12-24) ── Very wide, stocky torso
1093
+ // Outline
1094
+ outlineRect(ctx, 5, 12, 22, 14, G.outline, s, ox, oy);
1095
+ // Body fill — dark brown base
1096
+ px(ctx, 5, 12, 22, 14, G.furDark, s, ox, oy);
1097
+ // Main fur color on upper body
1098
+ px(ctx, 6, 13, 20, 8, G.furMain, s, ox, oy);
1099
+ // Light highlights on top (back ridge)
1100
+ px(ctx, 8, 12, 16, 2, G.furLight, s, ox, oy);
1101
+ // Lighter chest/belly underneath
1102
+ px(ctx, 9, 19, 14, 6, G.furChest, s, ox, oy);
1103
+ // Dithered transition from main fur to chest
1104
+ dither(ctx, 9, 18, 14, 1, G.furMain, G.furChest, s, ox, oy);
1105
+ // Dark underside shadow
1106
+ px(ctx, 6, 25, 20, 1, G.skinDark, s, ox, oy);
1107
+ // ── Front arms (rows 14-28): thick, reaching to ground ──
1108
+ // Left front arm
1109
+ const laOff = leftArmFwd;
1110
+ outlineRect(ctx, 2 + laOff, 14, 6, 14, G.outline, s, ox, oy);
1111
+ px(ctx, 2 + laOff, 14, 6, 14, G.furDark, s, ox, oy);
1112
+ px(ctx, 3 + laOff, 14, 4, 12, G.furMain, s, ox, oy);
1113
+ // Shoulder highlight
1114
+ px(ctx, 3 + laOff, 14, 4, 2, G.furLight, s, ox, oy);
1115
+ // Forearm darker
1116
+ px(ctx, 3 + laOff, 22, 4, 4, G.skinDark, s, ox, oy);
1117
+ // Hand/knuckles
1118
+ px(ctx, 1 + laOff, 28, 7, 3, G.furDark, s, ox, oy);
1119
+ px(ctx, 2 + laOff, 28, 5, 2, G.furMain, s, ox, oy);
1120
+ // Claws
1121
+ px(ctx, 1 + laOff, 31, 1, 1, G.claws, s, ox, oy);
1122
+ px(ctx, 3 + laOff, 31, 1, 1, G.claws, s, ox, oy);
1123
+ px(ctx, 5 + laOff, 31, 1, 1, G.claws, s, ox, oy);
1124
+ px(ctx, 7 + laOff, 31, 1, 1, G.claws, s, ox, oy);
1125
+ // Right front arm
1126
+ const raOff = rightArmFwd;
1127
+ outlineRect(ctx, 24 + raOff, 14, 6, 14, G.outline, s, ox, oy);
1128
+ px(ctx, 24 + raOff, 14, 6, 14, G.furDark, s, ox, oy);
1129
+ px(ctx, 25 + raOff, 14, 4, 12, G.furMain, s, ox, oy);
1130
+ // Shoulder highlight
1131
+ px(ctx, 25 + raOff, 14, 4, 2, G.furLight, s, ox, oy);
1132
+ // Forearm darker
1133
+ px(ctx, 25 + raOff, 22, 4, 4, G.skinDark, s, ox, oy);
1134
+ // Hand/knuckles
1135
+ px(ctx, 24 + raOff, 28, 7, 3, G.furDark, s, ox, oy);
1136
+ px(ctx, 25 + raOff, 28, 5, 2, G.furMain, s, ox, oy);
1137
+ // Claws
1138
+ px(ctx, 24 + raOff, 31, 1, 1, G.claws, s, ox, oy);
1139
+ px(ctx, 26 + raOff, 31, 1, 1, G.claws, s, ox, oy);
1140
+ px(ctx, 28 + raOff, 31, 1, 1, G.claws, s, ox, oy);
1141
+ px(ctx, 30 + raOff, 31, 1, 1, G.claws, s, ox, oy);
1142
+ // ── Head (rows 3-12): big round head ──
1143
+ const hx = 7 + headTilt;
1144
+ const hy = 3;
1145
+ // Outline
1146
+ outlineRect(ctx, hx, hy, 18, 10, G.outline, s, ox, oy);
1147
+ // Head fill — dark base
1148
+ px(ctx, hx, hy, 18, 10, G.furDark, s, ox, oy);
1149
+ // Main fur
1150
+ px(ctx, hx + 1, hy + 1, 16, 8, G.furMain, s, ox, oy);
1151
+ // Brow ridge highlight
1152
+ px(ctx, hx + 2, hy + 1, 14, 2, G.furLight, s, ox, oy);
1153
+ // Lighter face area (center)
1154
+ px(ctx, hx + 4, hy + 4, 10, 5, G.furChest, s, ox, oy);
1155
+ // Darker brow ridge above eyes (makes them look grumpy/narrowed)
1156
+ px(ctx, hx + 3, hy + 3, 12, 2, G.skinDark, s, ox, oy);
1157
+ // ── Eyes (rows 7-9 relative, hy+4 to hy+6 in head) ──
1158
+ const eyeY = hy + 5;
1159
+ if (eyeState === 'blink') {
1160
+ // Closed eyes — thin line
1161
+ px(ctx, hx + 5, eyeY, 3, 1, G.outline, s, ox, oy);
1162
+ px(ctx, hx + 11, eyeY, 3, 1, G.outline, s, ox, oy);
1163
+ }
1164
+ else if (eyeState === 'narrow') {
1165
+ // Narrowed/grumpy eyes — 3x2, squinted
1166
+ // Eye whites (narrow slit)
1167
+ px(ctx, hx + 5, eyeY, 3, 2, G.eyeWhite, s, ox, oy);
1168
+ px(ctx, hx + 11, eyeY, 3, 2, G.eyeWhite, s, ox, oy);
1169
+ // Pupils
1170
+ px(ctx, hx + 6, eyeY, 2, 2, G.eyePupil, s, ox, oy);
1171
+ px(ctx, hx + 12, eyeY, 2, 2, G.eyePupil, s, ox, oy);
1172
+ // Heavy brow line pushing down (grumpy)
1173
+ px(ctx, hx + 4, eyeY - 1, 5, 1, G.skinDark, s, ox, oy);
1174
+ px(ctx, hx + 10, eyeY - 1, 5, 1, G.skinDark, s, ox, oy);
1175
+ }
1176
+ else if (eyeState === 'up') {
1177
+ // Looking up
1178
+ px(ctx, hx + 5, eyeY - 1, 3, 2, G.eyeWhite, s, ox, oy);
1179
+ px(ctx, hx + 11, eyeY - 1, 3, 2, G.eyeWhite, s, ox, oy);
1180
+ px(ctx, hx + 6, eyeY - 1, 1, 1, G.eyePupil, s, ox, oy);
1181
+ px(ctx, hx + 12, eyeY - 1, 1, 1, G.eyePupil, s, ox, oy);
1182
+ }
1183
+ else if (eyeState === 'wide') {
1184
+ // Wide/surprised
1185
+ px(ctx, hx + 5, eyeY - 1, 3, 3, G.eyeWhite, s, ox, oy);
1186
+ px(ctx, hx + 11, eyeY - 1, 3, 3, G.eyeWhite, s, ox, oy);
1187
+ px(ctx, hx + 6, eyeY, 1, 1, G.eyePupil, s, ox, oy);
1188
+ px(ctx, hx + 12, eyeY, 1, 1, G.eyePupil, s, ox, oy);
1189
+ }
1190
+ else {
1191
+ // Normal eyes
1192
+ px(ctx, hx + 5, eyeY, 3, 2, G.eyeWhite, s, ox, oy);
1193
+ px(ctx, hx + 11, eyeY, 3, 2, G.eyeWhite, s, ox, oy);
1194
+ px(ctx, hx + 6, eyeY, 2, 2, G.eyePupil, s, ox, oy);
1195
+ px(ctx, hx + 12, eyeY, 2, 2, G.eyePupil, s, ox, oy);
1196
+ }
1197
+ // ── Nose ──
1198
+ px(ctx, hx + 8, hy + 7, 3, 2, G.nose, s, ox, oy);
1199
+ // Nostrils
1200
+ px(ctx, hx + 8, hy + 8, 1, 1, G.outline, s, ox, oy);
1201
+ px(ctx, hx + 10, hy + 8, 1, 1, G.outline, s, ox, oy);
1202
+ // ── Mouth (rows 10-11 of head) ──
1203
+ const mouthY = hy + 9;
1204
+ const mouthX = hx + 6;
1205
+ if (mouthOpen === 0) {
1206
+ // Closed grumpy mouth — wide line with downturned ends
1207
+ px(ctx, mouthX, mouthY, 7, 1, G.mouth, s, ox, oy);
1208
+ // Fangs poking down
1209
+ px(ctx, mouthX + 1, mouthY + 1, 1, 1, G.fang, s, ox, oy);
1210
+ px(ctx, mouthX + 5, mouthY + 1, 1, 1, G.fang, s, ox, oy);
1211
+ }
1212
+ else if (mouthOpen === 1) {
1213
+ // Half open
1214
+ px(ctx, mouthX, mouthY, 7, 1, G.mouth, s, ox, oy);
1215
+ px(ctx, mouthX + 1, mouthY + 1, 5, 1, G.mouth, s, ox, oy);
1216
+ // Fangs
1217
+ px(ctx, mouthX, mouthY + 1, 1, 1, G.fang, s, ox, oy);
1218
+ px(ctx, mouthX + 6, mouthY + 1, 1, 1, G.fang, s, ox, oy);
1219
+ }
1220
+ else if (mouthOpen === 2) {
1221
+ // Wide open — show red inside
1222
+ px(ctx, mouthX - 1, mouthY, 9, 2, G.mouth, s, ox, oy);
1223
+ px(ctx, mouthX, mouthY, 7, 2, '#8B2020', s, ox, oy); // red inner
1224
+ // Big fangs
1225
+ px(ctx, mouthX - 1, mouthY, 1, 2, G.fang, s, ox, oy);
1226
+ px(ctx, mouthX + 7, mouthY, 1, 2, G.fang, s, ox, oy);
1227
+ }
1228
+ else {
1229
+ // Closed tight line
1230
+ px(ctx, mouthX + 1, mouthY, 5, 1, G.mouth, s, ox, oy);
1231
+ }
1232
+ // ── Baseball cap (rows 0-5): red with white front panel ──
1233
+ const capX = hx - 1 + capTilt;
1234
+ const capY = hy - 3;
1235
+ // Cap crown — red
1236
+ px(ctx, capX + 2, capY, 16, 2, G.capRed, s, ox, oy);
1237
+ px(ctx, capX + 1, capY + 2, 18, 2, G.capRed, s, ox, oy);
1238
+ // White front panel (left portion — cap is backwards/sideways)
1239
+ px(ctx, capX + 2, capY, 5, 2, G.capWhite, s, ox, oy);
1240
+ px(ctx, capX + 1, capY + 2, 6, 2, G.capWhite, s, ox, oy);
1241
+ // Brim extending to the right (cap worn sideways)
1242
+ px(ctx, capX + 18, capY + 3, 4, 2, G.capRed, s, ox, oy);
1243
+ px(ctx, capX + 18, capY + 4, 4, 1, G.capBrim, s, ox, oy); // brim underside
1244
+ // Cap outline
1245
+ px(ctx, capX + 2, capY - 1, 16, 1, G.outline, s, ox, oy); // top
1246
+ px(ctx, capX, capY + 2, 1, 2, G.outline, s, ox, oy); // left side
1247
+ px(ctx, capX + 1, capY + 4, 18, 1, G.outline, s, ox, oy); // bottom band
1248
+ px(ctx, capX + 22, capY + 3, 1, 2, G.outline, s, ox, oy); // brim end
1249
+ // Cap button on top
1250
+ px(ctx, capX + 9, capY - 1, 2, 1, G.capRed, s, ox, oy);
1251
+ // ── Question mark particle (thinking frame 2) ──
1252
+ if (questionMark) {
1253
+ const qColor = getMoodColor('thinking', frame, moodColor);
1254
+ px(ctx, hx + 6, hy - 6, 3, 1, qColor, s, ox, oy);
1255
+ px(ctx, hx + 8, hy - 5, 1, 1, qColor, s, ox, oy);
1256
+ px(ctx, hx + 7, hy - 4, 1, 1, qColor, s, ox, oy);
1257
+ px(ctx, hx + 7, hy - 2, 1, 1, qColor, s, ox, oy);
1258
+ }
1259
+ }
1260
+ /**
1261
+ * Draw animated mood particles around the gorilla.
1262
+ * Same interface as drawMoodParticles but tuned for gorilla position/shape.
1263
+ */
1264
+ export function drawGorillaParticles(ctx, x, y, scale, mood, frame) {
1265
+ const s = scale;
1266
+ const color = getMoodColor(mood, frame);
1267
+ if (mood === 'dancing') {
1268
+ // Music notes floating up
1269
+ const notes = [
1270
+ { baseX: -2, baseY: 2, phase: 0 },
1271
+ { baseX: 30, baseY: 0, phase: 2 },
1272
+ { baseX: 14, baseY: -2, phase: 4 },
1273
+ ];
1274
+ for (const note of notes) {
1275
+ const floatY = ((frame + note.phase) % 8) * -2;
1276
+ const ny = note.baseY + floatY;
1277
+ if (ny > -8) {
1278
+ const c = RAINBOW[(frame + note.phase) % RAINBOW.length];
1279
+ px(ctx, note.baseX, ny + 3, 2, 2, c, s, x, y);
1280
+ px(ctx, note.baseX + 1, ny, 1, 3, c, s, x, y);
1281
+ px(ctx, note.baseX + 1, ny, 2, 1, c, s, x, y);
1282
+ }
1283
+ }
1284
+ }
1285
+ else if (mood === 'excited') {
1286
+ // Sparkle + shapes
1287
+ const positions = [
1288
+ { x: -2, y: 4 }, { x: 30, y: 2 },
1289
+ { x: 2, y: -4 }, { x: 28, y: -2 },
1290
+ ];
1291
+ for (let i = 0; i < positions.length; i++) {
1292
+ const visible = ((frame + i * 2) % 4) < 2;
1293
+ if (!visible)
1294
+ continue;
1295
+ const p = positions[i];
1296
+ px(ctx, p.x + 1, p.y, 1, 3, color, s, x, y);
1297
+ px(ctx, p.x, p.y + 1, 3, 1, color, s, x, y);
1298
+ }
1299
+ }
1300
+ else if (mood === 'thinking') {
1301
+ // Thought bubbles
1302
+ const f = frame % 3;
1303
+ if (f === 0) {
1304
+ px(ctx, 20, -6, 3, 1, color, s, x, y);
1305
+ px(ctx, 22, -5, 1, 1, color, s, x, y);
1306
+ px(ctx, 21, -4, 1, 1, color, s, x, y);
1307
+ px(ctx, 21, -2, 1, 1, color, s, x, y);
1308
+ }
1309
+ else if (f === 1) {
1310
+ px(ctx, 19, -4, 1, 1, color, s, x, y);
1311
+ px(ctx, 21, -5, 1, 1, color, s, x, y);
1312
+ px(ctx, 23, -4, 1, 1, color, s, x, y);
1313
+ }
1314
+ }
1315
+ else if (mood === 'talking') {
1316
+ // Sound waves from mouth area
1317
+ const baseX = 26;
1318
+ const baseY = 14;
1319
+ for (let i = 0; i < 3; i++) {
1320
+ const visible = ((frame + i) % 4) < 3;
1321
+ if (!visible)
1322
+ continue;
1323
+ const dist = i * 2 + ((frame % 2) * 1);
1324
+ const alpha = 1 - i * 0.3;
1325
+ const c = dimColor(color.startsWith('rgb') ? '#58a6ff' : color, alpha);
1326
+ px(ctx, baseX + dist, baseY - 1, 1, 1, c, s, x, y);
1327
+ px(ctx, baseX + dist + 1, baseY, 1, 1, c, s, x, y);
1328
+ px(ctx, baseX + dist, baseY + 1, 1, 1, c, s, x, y);
1329
+ }
1330
+ }
1331
+ }
898
1332
  // ─── Mood Particles ────────────────────────────────────────────
899
1333
  /**
900
1334
  * Draw animated mood particles around the robot.
@@ -0,0 +1,56 @@
1
+ export interface ViewerMemory {
2
+ username: string;
3
+ firstSeen: string;
4
+ totalMessages: number;
5
+ topics: string[];
6
+ personality_notes: string;
7
+ lastInteraction: string;
8
+ }
9
+ export interface ChatAIStats {
10
+ totalMessages: number;
11
+ totalResponses: number;
12
+ uniqueViewers: number;
13
+ currentMode: string;
14
+ currentTopic: string;
15
+ uptime: number;
16
+ modelInUse: string;
17
+ queueDepth: number;
18
+ }
19
+ export declare class StreamChatAI {
20
+ private mode;
21
+ private viewers;
22
+ private contextHistory;
23
+ private currentTopic;
24
+ private topicHistory;
25
+ private lastResponseTime;
26
+ private messagesSinceResponse;
27
+ private totalMessages;
28
+ private totalResponses;
29
+ private startTime;
30
+ private modelInUse;
31
+ private queue;
32
+ private activeTriviaQuestion;
33
+ private processing;
34
+ constructor();
35
+ processMessage(username: string, message: string, platform: string): Promise<string | null>;
36
+ private shouldRespond;
37
+ private processQueue;
38
+ private generateChatResponse;
39
+ private handleGreeting;
40
+ private handleCompliment;
41
+ handleCommand(cmd: string, args: string, username: string): Promise<string>;
42
+ private handleAsk;
43
+ private handleJoke;
44
+ private handleTrivia;
45
+ private touchViewer;
46
+ private isNewOrReturning;
47
+ setMode(mode: 'reactive' | 'conversational' | 'entertainer' | 'quiet'): void;
48
+ getMode(): string;
49
+ getViewerMemory(username: string): ViewerMemory | null;
50
+ getTopicSummary(): string;
51
+ getStats(): ChatAIStats;
52
+ saveMemory(): void;
53
+ loadMemory(): void;
54
+ }
55
+ export declare function registerStreamChatAITools(): void;
56
+ //# sourceMappingURL=stream-chat-ai.d.ts.map