ansimax 1.1.1 → 1.2.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.
package/dist/index.js CHANGED
@@ -46,6 +46,7 @@ __export(index_exports, {
46
46
  ST: () => ST,
47
47
  STYLE: () => STYLE,
48
48
  animate: () => animate,
49
+ animateGradient: () => animateGradient,
49
50
  ascii: () => ascii,
50
51
  bell: () => bell,
51
52
  bg256: () => bg256,
@@ -225,6 +226,11 @@ var cursor = {
225
226
  var _exitHandlerRegistered = false;
226
227
  var _isTestEnv = () => process.env["JEST_WORKER_ID"] !== void 0 || process.env["VITEST"] !== void 0 || process.env["NODE_ENV"] === "test";
227
228
  var _installCursorRestoreImpl = () => {
229
+ try {
230
+ const current = process.getMaxListeners?.() ?? 10;
231
+ if (current < 20) process.setMaxListeners?.(20);
232
+ } catch {
233
+ }
228
234
  const restore = () => {
229
235
  try {
230
236
  safeStreamWrite(process.stdout, cursor.show());
@@ -1109,6 +1115,37 @@ var compose = (...fns) => (text) => {
1109
1115
  if (opens === "") return s;
1110
1116
  return opens + s + reset();
1111
1117
  };
1118
+ var _cubicBezier = (t, x1 = 0.25, y1 = 0.1, x2 = 0.25, y2 = 1) => {
1119
+ if (t <= 0) return 0;
1120
+ if (t >= 1) return 1;
1121
+ let s = t;
1122
+ for (let i = 0; i < 8; i++) {
1123
+ const s22 = s * s;
1124
+ const s32 = s22 * s;
1125
+ const oneMinusS = 1 - s;
1126
+ const oneMinusS2 = oneMinusS * oneMinusS;
1127
+ const fx = 3 * oneMinusS2 * s * x1 + 3 * oneMinusS * s22 * x2 + s32 - t;
1128
+ const dfx = 3 * oneMinusS2 * x1 + 6 * oneMinusS * s * (x2 - x1) + 3 * s22 * (1 - x2);
1129
+ if (Math.abs(dfx) < 1e-6) break;
1130
+ s -= fx / dfx;
1131
+ s = Math.min(1, Math.max(0, s));
1132
+ }
1133
+ const s2 = s * s;
1134
+ const s3 = s2 * s;
1135
+ return 3 * (1 - s) * (1 - s) * s * y1 + 3 * (1 - s) * s2 * y2 + s3;
1136
+ };
1137
+ var EASINGS = {
1138
+ "linear": (t) => t,
1139
+ "ease-in": (t) => t * t,
1140
+ "ease-out": (t) => 1 - (1 - t) * (1 - t),
1141
+ "ease-in-out": (t) => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2,
1142
+ "cubic-bezier": (t) => _cubicBezier(t)
1143
+ };
1144
+ var resolveEasing = (e) => {
1145
+ if (typeof e === "function") return e;
1146
+ if (typeof e === "string" && EASINGS[e]) return EASINGS[e];
1147
+ return EASINGS.linear;
1148
+ };
1112
1149
  var stripAnsi3 = stripAnsi;
1113
1150
  var gradient = (text, stops, opts = {}) => {
1114
1151
  const s = coerceText(text);
@@ -1120,13 +1157,15 @@ var gradient = (text, stops, opts = {}) => {
1120
1157
  const c = colors[0];
1121
1158
  return adaptiveFg(c.r, c.g, c.b) + s + reset();
1122
1159
  }
1123
- const { preserveAnsi = false } = opts;
1160
+ const { preserveAnsi = false, easing, phase = 0 } = opts;
1161
+ const easingFn = resolveEasing(easing);
1162
+ const phaseN = Number.isFinite(phase) ? (phase % 1 + 1) % 1 : 0;
1124
1163
  if (!preserveAnsi || !s.includes("\x1B")) {
1125
- return _gradientPlain(s, colors);
1164
+ return _gradientPlain(s, colors, easingFn, phaseN);
1126
1165
  }
1127
- return _gradientAnsiAware(s, colors);
1166
+ return _gradientAnsiAware(s, colors, easingFn, phaseN);
1128
1167
  };
1129
- var _gradientPlain = (text, colors) => {
1168
+ var _gradientPlain = (text, colors, easingFn, phase) => {
1130
1169
  const chars = [...text];
1131
1170
  const visible = chars.filter((c) => c !== " ").length;
1132
1171
  if (visible === 0) return text;
@@ -1139,7 +1178,10 @@ var _gradientPlain = (text, colors) => {
1139
1178
  out += ch;
1140
1179
  continue;
1141
1180
  }
1142
- const t = visible === 1 ? 0 : colorIdx / visibleMinus;
1181
+ let t = visible === 1 ? 0 : colorIdx / visibleMinus;
1182
+ t = (t + phase) % 1;
1183
+ t = easingFn(t);
1184
+ t = Math.min(1, Math.max(0, t));
1143
1185
  const scaled = t * (colorCount - 1);
1144
1186
  const lo = Math.floor(scaled);
1145
1187
  const hi = Math.min(lo + 1, colorCount - 1);
@@ -1150,7 +1192,7 @@ var _gradientPlain = (text, colors) => {
1150
1192
  return out;
1151
1193
  };
1152
1194
  var ANSI_TOKEN = /\x1b\[[0-9;?]*[a-zA-Z]/y;
1153
- var _gradientAnsiAware = (text, colors) => {
1195
+ var _gradientAnsiAware = (text, colors, easingFn, phase) => {
1154
1196
  const visible = stripAnsi3(text).split("").filter((c) => c !== " ").length;
1155
1197
  if (visible === 0) return text;
1156
1198
  let out = "";
@@ -1176,7 +1218,10 @@ var _gradientAnsiAware = (text, colors) => {
1176
1218
  if (ch === " ") {
1177
1219
  out += ch;
1178
1220
  } else {
1179
- const t = visible === 1 ? 0 : colorIdx / visibleMinus;
1221
+ let t = visible === 1 ? 0 : colorIdx / visibleMinus;
1222
+ t = (t + phase) % 1;
1223
+ t = easingFn(t);
1224
+ t = Math.min(1, Math.max(0, t));
1180
1225
  const scaled = t * (colorCount - 1);
1181
1226
  const lo = Math.floor(scaled);
1182
1227
  const hi = Math.min(lo + 1, colorCount - 1);
@@ -1190,6 +1235,76 @@ var _gradientAnsiAware = (text, colors) => {
1190
1235
  };
1191
1236
  var RAINBOW = ["#ff0000", "#ff7f00", "#ffff00", "#00ff00", "#0000ff", "#8b00ff"];
1192
1237
  var rainbow = (text) => gradient(text, RAINBOW);
1238
+ var animateGradient = (text, stops, opts = {}) => {
1239
+ const {
1240
+ duration = 2e3,
1241
+ fps = 30,
1242
+ infinite = true,
1243
+ cycles = 1,
1244
+ direction = "forward",
1245
+ easing,
1246
+ preserveAnsi = false,
1247
+ signal,
1248
+ onFrame
1249
+ } = opts;
1250
+ const safeFps = Math.min(60, Math.max(1, Number.isFinite(fps) ? fps : 30));
1251
+ const safeDuration2 = Math.max(100, Number.isFinite(duration) ? duration : 2e3);
1252
+ const safeCycles = Math.max(1, Number.isFinite(cycles) ? cycles : 1);
1253
+ const frameInterval2 = 1e3 / safeFps;
1254
+ let stopped = false;
1255
+ let timer = null;
1256
+ let resolveDone = (
1257
+ /* istanbul ignore next */
1258
+ () => {
1259
+ }
1260
+ );
1261
+ const done = new Promise((res) => {
1262
+ resolveDone = res;
1263
+ });
1264
+ const stop = () => {
1265
+ if (stopped) return;
1266
+ stopped = true;
1267
+ if (timer != null) {
1268
+ clearInterval(timer);
1269
+ timer = null;
1270
+ }
1271
+ resolveDone();
1272
+ };
1273
+ if (signal) {
1274
+ if (signal.aborted) {
1275
+ stop();
1276
+ return { stop, done };
1277
+ }
1278
+ signal.addEventListener("abort", stop, { once: true });
1279
+ }
1280
+ const startTime = Date.now();
1281
+ const renderFrame = () => {
1282
+ if (stopped) return;
1283
+ const elapsed = Date.now() - startTime;
1284
+ let phase = elapsed / safeDuration2 % 1;
1285
+ if (direction === "reverse") phase = 1 - phase;
1286
+ const frame = gradient(text, stops, { preserveAnsi, easing, phase });
1287
+ if (onFrame) {
1288
+ onFrame(frame, phase);
1289
+ } else {
1290
+ try {
1291
+ process.stdout.write("\r" + frame);
1292
+ } catch {
1293
+ }
1294
+ }
1295
+ if (!infinite) {
1296
+ const completedCycles = Math.floor(elapsed / safeDuration2);
1297
+ if (completedCycles >= safeCycles) {
1298
+ stop();
1299
+ }
1300
+ }
1301
+ };
1302
+ renderFrame();
1303
+ if (!stopped) {
1304
+ timer = setInterval(renderFrame, frameInterval2);
1305
+ }
1306
+ return { stop, done };
1307
+ };
1193
1308
  var PRESET_DEFS = {
1194
1309
  sunset: ["#ff6b6b", "#feca57", "#48dbfb"],
1195
1310
  ocean: ["#0575e6", "#021b79"],
@@ -4113,12 +4228,12 @@ var validateTheme = (t) => {
4113
4228
  }
4114
4229
  const obj = t;
4115
4230
  if (typeof obj.name !== "string" || obj.name.length === 0) {
4116
- throw new Error('Theme must have a non-empty "name" string.');
4231
+ throw new TypeError('Theme must have a non-empty "name" string.');
4117
4232
  }
4118
4233
  for (const key of REQUIRED_COLOR_KEYS) {
4119
4234
  const value = obj[key];
4120
4235
  if (typeof value !== "string" || !HEX_RE4.test(value)) {
4121
- throw new Error(
4236
+ throw new TypeError(
4122
4237
  `Invalid hex in theme "${obj.name}" \u2192 ${key}: ${JSON.stringify(value)}. Expected #RGB or #RRGGBB.`
4123
4238
  );
4124
4239
  }
@@ -4127,20 +4242,20 @@ var validateTheme = (t) => {
4127
4242
  const value = obj[key];
4128
4243
  if (value === void 0) continue;
4129
4244
  if (typeof value !== "string" || !HEX_RE4.test(value)) {
4130
- throw new Error(
4245
+ throw new TypeError(
4131
4246
  `Invalid hex in theme "${obj.name}" \u2192 ${key}: ${JSON.stringify(value)}.`
4132
4247
  );
4133
4248
  }
4134
4249
  }
4135
4250
  if (!Array.isArray(obj.gradient) || obj.gradient.length < 2) {
4136
- throw new Error(
4251
+ throw new TypeError(
4137
4252
  `Theme "${obj.name}" must define a "gradient" array with at least 2 colors.`
4138
4253
  );
4139
4254
  }
4140
4255
  for (let i = 0; i < obj.gradient.length; i++) {
4141
4256
  const stop = obj.gradient[i];
4142
4257
  if (typeof stop !== "string" || !HEX_RE4.test(stop)) {
4143
- throw new Error(
4258
+ throw new TypeError(
4144
4259
  `Invalid hex in theme "${obj.name}" \u2192 gradient[${i}]: ${JSON.stringify(stop)}.`
4145
4260
  );
4146
4261
  }
@@ -4452,8 +4567,8 @@ var themes = {
4452
4567
  use(name) {
4453
4568
  const t = _globalRegistry.get(name);
4454
4569
  if (!t) {
4455
- throw new Error(
4456
- `Theme "${name}" not found. Available: ${[..._globalRegistry.keys()].join(", ")}`
4570
+ throw new RangeError(
4571
+ `Theme "${name}" not found. Available themes: ${[..._globalRegistry.keys()].join(", ")}`
4457
4572
  );
4458
4573
  }
4459
4574
  const old = _globalActive;
@@ -4828,6 +4943,7 @@ var gradientRect = (opts = {}) => {
4828
4943
  colors = ["#ff0000", "#0000ff"],
4829
4944
  style = "horizontal",
4830
4945
  angle,
4946
+ startAngle = 0,
4831
4947
  dither = "none",
4832
4948
  braille = false
4833
4949
  } = opts;
@@ -4866,7 +4982,15 @@ var gradientRect = (opts = {}) => {
4866
4982
  } else if (style === "horizontal") t = col / (safeW - 1);
4867
4983
  else if (style === "vertical") t = row / (safeH - 1);
4868
4984
  else if (style === "diagonal") t = (col + row) / (safeW + safeH - 2);
4869
- else {
4985
+ else if (style === "conic") {
4986
+ const cx = safeW / 2, cy = safeH / 2;
4987
+ const dx = col - cx;
4988
+ const dy = row - cy;
4989
+ const startRad = (Number.isFinite(startAngle) ? startAngle : 0) * Math.PI / 180;
4990
+ let angleRad = Math.atan2(dy, dx) - startRad;
4991
+ angleRad = (angleRad % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
4992
+ t = angleRad / (2 * Math.PI);
4993
+ } else {
4870
4994
  const cx = safeW / 2, cy = safeH / 2;
4871
4995
  const dx = (col - cx) / cx;
4872
4996
  const dy = (row - cy) / cy;
@@ -5318,6 +5442,7 @@ var index_default = ansimax;
5318
5442
  ST,
5319
5443
  STYLE,
5320
5444
  animate,
5445
+ animateGradient,
5321
5446
  ascii,
5322
5447
  bell,
5323
5448
  bg256,
package/dist/index.mjs CHANGED
@@ -53,6 +53,11 @@ var cursor = {
53
53
  var _exitHandlerRegistered = false;
54
54
  var _isTestEnv = () => process.env["JEST_WORKER_ID"] !== void 0 || process.env["VITEST"] !== void 0 || process.env["NODE_ENV"] === "test";
55
55
  var _installCursorRestoreImpl = () => {
56
+ try {
57
+ const current = process.getMaxListeners?.() ?? 10;
58
+ if (current < 20) process.setMaxListeners?.(20);
59
+ } catch {
60
+ }
56
61
  const restore = () => {
57
62
  try {
58
63
  safeStreamWrite(process.stdout, cursor.show());
@@ -937,6 +942,37 @@ var compose = (...fns) => (text) => {
937
942
  if (opens === "") return s;
938
943
  return opens + s + reset();
939
944
  };
945
+ var _cubicBezier = (t, x1 = 0.25, y1 = 0.1, x2 = 0.25, y2 = 1) => {
946
+ if (t <= 0) return 0;
947
+ if (t >= 1) return 1;
948
+ let s = t;
949
+ for (let i = 0; i < 8; i++) {
950
+ const s22 = s * s;
951
+ const s32 = s22 * s;
952
+ const oneMinusS = 1 - s;
953
+ const oneMinusS2 = oneMinusS * oneMinusS;
954
+ const fx = 3 * oneMinusS2 * s * x1 + 3 * oneMinusS * s22 * x2 + s32 - t;
955
+ const dfx = 3 * oneMinusS2 * x1 + 6 * oneMinusS * s * (x2 - x1) + 3 * s22 * (1 - x2);
956
+ if (Math.abs(dfx) < 1e-6) break;
957
+ s -= fx / dfx;
958
+ s = Math.min(1, Math.max(0, s));
959
+ }
960
+ const s2 = s * s;
961
+ const s3 = s2 * s;
962
+ return 3 * (1 - s) * (1 - s) * s * y1 + 3 * (1 - s) * s2 * y2 + s3;
963
+ };
964
+ var EASINGS = {
965
+ "linear": (t) => t,
966
+ "ease-in": (t) => t * t,
967
+ "ease-out": (t) => 1 - (1 - t) * (1 - t),
968
+ "ease-in-out": (t) => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2,
969
+ "cubic-bezier": (t) => _cubicBezier(t)
970
+ };
971
+ var resolveEasing = (e) => {
972
+ if (typeof e === "function") return e;
973
+ if (typeof e === "string" && EASINGS[e]) return EASINGS[e];
974
+ return EASINGS.linear;
975
+ };
940
976
  var stripAnsi3 = stripAnsi;
941
977
  var gradient = (text, stops, opts = {}) => {
942
978
  const s = coerceText(text);
@@ -948,13 +984,15 @@ var gradient = (text, stops, opts = {}) => {
948
984
  const c = colors[0];
949
985
  return adaptiveFg(c.r, c.g, c.b) + s + reset();
950
986
  }
951
- const { preserveAnsi = false } = opts;
987
+ const { preserveAnsi = false, easing, phase = 0 } = opts;
988
+ const easingFn = resolveEasing(easing);
989
+ const phaseN = Number.isFinite(phase) ? (phase % 1 + 1) % 1 : 0;
952
990
  if (!preserveAnsi || !s.includes("\x1B")) {
953
- return _gradientPlain(s, colors);
991
+ return _gradientPlain(s, colors, easingFn, phaseN);
954
992
  }
955
- return _gradientAnsiAware(s, colors);
993
+ return _gradientAnsiAware(s, colors, easingFn, phaseN);
956
994
  };
957
- var _gradientPlain = (text, colors) => {
995
+ var _gradientPlain = (text, colors, easingFn, phase) => {
958
996
  const chars = [...text];
959
997
  const visible = chars.filter((c) => c !== " ").length;
960
998
  if (visible === 0) return text;
@@ -967,7 +1005,10 @@ var _gradientPlain = (text, colors) => {
967
1005
  out += ch;
968
1006
  continue;
969
1007
  }
970
- const t = visible === 1 ? 0 : colorIdx / visibleMinus;
1008
+ let t = visible === 1 ? 0 : colorIdx / visibleMinus;
1009
+ t = (t + phase) % 1;
1010
+ t = easingFn(t);
1011
+ t = Math.min(1, Math.max(0, t));
971
1012
  const scaled = t * (colorCount - 1);
972
1013
  const lo = Math.floor(scaled);
973
1014
  const hi = Math.min(lo + 1, colorCount - 1);
@@ -978,7 +1019,7 @@ var _gradientPlain = (text, colors) => {
978
1019
  return out;
979
1020
  };
980
1021
  var ANSI_TOKEN = /\x1b\[[0-9;?]*[a-zA-Z]/y;
981
- var _gradientAnsiAware = (text, colors) => {
1022
+ var _gradientAnsiAware = (text, colors, easingFn, phase) => {
982
1023
  const visible = stripAnsi3(text).split("").filter((c) => c !== " ").length;
983
1024
  if (visible === 0) return text;
984
1025
  let out = "";
@@ -1004,7 +1045,10 @@ var _gradientAnsiAware = (text, colors) => {
1004
1045
  if (ch === " ") {
1005
1046
  out += ch;
1006
1047
  } else {
1007
- const t = visible === 1 ? 0 : colorIdx / visibleMinus;
1048
+ let t = visible === 1 ? 0 : colorIdx / visibleMinus;
1049
+ t = (t + phase) % 1;
1050
+ t = easingFn(t);
1051
+ t = Math.min(1, Math.max(0, t));
1008
1052
  const scaled = t * (colorCount - 1);
1009
1053
  const lo = Math.floor(scaled);
1010
1054
  const hi = Math.min(lo + 1, colorCount - 1);
@@ -1018,6 +1062,76 @@ var _gradientAnsiAware = (text, colors) => {
1018
1062
  };
1019
1063
  var RAINBOW = ["#ff0000", "#ff7f00", "#ffff00", "#00ff00", "#0000ff", "#8b00ff"];
1020
1064
  var rainbow = (text) => gradient(text, RAINBOW);
1065
+ var animateGradient = (text, stops, opts = {}) => {
1066
+ const {
1067
+ duration = 2e3,
1068
+ fps = 30,
1069
+ infinite = true,
1070
+ cycles = 1,
1071
+ direction = "forward",
1072
+ easing,
1073
+ preserveAnsi = false,
1074
+ signal,
1075
+ onFrame
1076
+ } = opts;
1077
+ const safeFps = Math.min(60, Math.max(1, Number.isFinite(fps) ? fps : 30));
1078
+ const safeDuration2 = Math.max(100, Number.isFinite(duration) ? duration : 2e3);
1079
+ const safeCycles = Math.max(1, Number.isFinite(cycles) ? cycles : 1);
1080
+ const frameInterval2 = 1e3 / safeFps;
1081
+ let stopped = false;
1082
+ let timer = null;
1083
+ let resolveDone = (
1084
+ /* istanbul ignore next */
1085
+ () => {
1086
+ }
1087
+ );
1088
+ const done = new Promise((res) => {
1089
+ resolveDone = res;
1090
+ });
1091
+ const stop = () => {
1092
+ if (stopped) return;
1093
+ stopped = true;
1094
+ if (timer != null) {
1095
+ clearInterval(timer);
1096
+ timer = null;
1097
+ }
1098
+ resolveDone();
1099
+ };
1100
+ if (signal) {
1101
+ if (signal.aborted) {
1102
+ stop();
1103
+ return { stop, done };
1104
+ }
1105
+ signal.addEventListener("abort", stop, { once: true });
1106
+ }
1107
+ const startTime = Date.now();
1108
+ const renderFrame = () => {
1109
+ if (stopped) return;
1110
+ const elapsed = Date.now() - startTime;
1111
+ let phase = elapsed / safeDuration2 % 1;
1112
+ if (direction === "reverse") phase = 1 - phase;
1113
+ const frame = gradient(text, stops, { preserveAnsi, easing, phase });
1114
+ if (onFrame) {
1115
+ onFrame(frame, phase);
1116
+ } else {
1117
+ try {
1118
+ process.stdout.write("\r" + frame);
1119
+ } catch {
1120
+ }
1121
+ }
1122
+ if (!infinite) {
1123
+ const completedCycles = Math.floor(elapsed / safeDuration2);
1124
+ if (completedCycles >= safeCycles) {
1125
+ stop();
1126
+ }
1127
+ }
1128
+ };
1129
+ renderFrame();
1130
+ if (!stopped) {
1131
+ timer = setInterval(renderFrame, frameInterval2);
1132
+ }
1133
+ return { stop, done };
1134
+ };
1021
1135
  var PRESET_DEFS = {
1022
1136
  sunset: ["#ff6b6b", "#feca57", "#48dbfb"],
1023
1137
  ocean: ["#0575e6", "#021b79"],
@@ -3941,12 +4055,12 @@ var validateTheme = (t) => {
3941
4055
  }
3942
4056
  const obj = t;
3943
4057
  if (typeof obj.name !== "string" || obj.name.length === 0) {
3944
- throw new Error('Theme must have a non-empty "name" string.');
4058
+ throw new TypeError('Theme must have a non-empty "name" string.');
3945
4059
  }
3946
4060
  for (const key of REQUIRED_COLOR_KEYS) {
3947
4061
  const value = obj[key];
3948
4062
  if (typeof value !== "string" || !HEX_RE4.test(value)) {
3949
- throw new Error(
4063
+ throw new TypeError(
3950
4064
  `Invalid hex in theme "${obj.name}" \u2192 ${key}: ${JSON.stringify(value)}. Expected #RGB or #RRGGBB.`
3951
4065
  );
3952
4066
  }
@@ -3955,20 +4069,20 @@ var validateTheme = (t) => {
3955
4069
  const value = obj[key];
3956
4070
  if (value === void 0) continue;
3957
4071
  if (typeof value !== "string" || !HEX_RE4.test(value)) {
3958
- throw new Error(
4072
+ throw new TypeError(
3959
4073
  `Invalid hex in theme "${obj.name}" \u2192 ${key}: ${JSON.stringify(value)}.`
3960
4074
  );
3961
4075
  }
3962
4076
  }
3963
4077
  if (!Array.isArray(obj.gradient) || obj.gradient.length < 2) {
3964
- throw new Error(
4078
+ throw new TypeError(
3965
4079
  `Theme "${obj.name}" must define a "gradient" array with at least 2 colors.`
3966
4080
  );
3967
4081
  }
3968
4082
  for (let i = 0; i < obj.gradient.length; i++) {
3969
4083
  const stop = obj.gradient[i];
3970
4084
  if (typeof stop !== "string" || !HEX_RE4.test(stop)) {
3971
- throw new Error(
4085
+ throw new TypeError(
3972
4086
  `Invalid hex in theme "${obj.name}" \u2192 gradient[${i}]: ${JSON.stringify(stop)}.`
3973
4087
  );
3974
4088
  }
@@ -4280,8 +4394,8 @@ var themes = {
4280
4394
  use(name) {
4281
4395
  const t = _globalRegistry.get(name);
4282
4396
  if (!t) {
4283
- throw new Error(
4284
- `Theme "${name}" not found. Available: ${[..._globalRegistry.keys()].join(", ")}`
4397
+ throw new RangeError(
4398
+ `Theme "${name}" not found. Available themes: ${[..._globalRegistry.keys()].join(", ")}`
4285
4399
  );
4286
4400
  }
4287
4401
  const old = _globalActive;
@@ -4656,6 +4770,7 @@ var gradientRect = (opts = {}) => {
4656
4770
  colors = ["#ff0000", "#0000ff"],
4657
4771
  style = "horizontal",
4658
4772
  angle,
4773
+ startAngle = 0,
4659
4774
  dither = "none",
4660
4775
  braille = false
4661
4776
  } = opts;
@@ -4694,7 +4809,15 @@ var gradientRect = (opts = {}) => {
4694
4809
  } else if (style === "horizontal") t = col / (safeW - 1);
4695
4810
  else if (style === "vertical") t = row / (safeH - 1);
4696
4811
  else if (style === "diagonal") t = (col + row) / (safeW + safeH - 2);
4697
- else {
4812
+ else if (style === "conic") {
4813
+ const cx = safeW / 2, cy = safeH / 2;
4814
+ const dx = col - cx;
4815
+ const dy = row - cy;
4816
+ const startRad = (Number.isFinite(startAngle) ? startAngle : 0) * Math.PI / 180;
4817
+ let angleRad = Math.atan2(dy, dx) - startRad;
4818
+ angleRad = (angleRad % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
4819
+ t = angleRad / (2 * Math.PI);
4820
+ } else {
4698
4821
  const cx = safeW / 2, cy = safeH / 2;
4699
4822
  const dx = (col - cx) / cx;
4700
4823
  const dy = (row - cy) / cy;
@@ -5145,6 +5268,7 @@ export {
5145
5268
  ST,
5146
5269
  STYLE,
5147
5270
  animate,
5271
+ animateGradient,
5148
5272
  ascii,
5149
5273
  bell,
5150
5274
  bg256,
@@ -118,7 +118,7 @@ async function main() {
118
118
  console.log(components.section('🏷️ Badges & Status', { width: 60 }));
119
119
  console.log();
120
120
  console.log(' ',
121
- components.badge('VERSION', 'v1.1.1'),
121
+ components.badge('VERSION', 'v1.2.0'),
122
122
  components.badge('BUILD', 'passing'),
123
123
  components.badge('LICENSE', 'Apache 2.0'));
124
124
  console.log();
@@ -117,7 +117,7 @@ console.log();
117
117
  console.log(components.section('🏷️ Badges & Status', { width: 60 }));
118
118
  console.log();
119
119
  console.log(' ',
120
- components.badge('VERSION', 'v1.1.1'),
120
+ components.badge('VERSION', 'v1.2.0'),
121
121
  components.badge('BUILD', 'passing'),
122
122
  components.badge('LICENSE', 'Apache 2.0'));
123
123
  console.log();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ansimax",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Zero-dependency CLI rendering library: colors, gradients, animations, ASCII art, pixel art, components, and themes \u2014 all in TypeScript.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",