ember-mug 0.1.3 → 0.1.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 (40) hide show
  1. package/dist/cli.js +37 -7
  2. package/dist/components/App.d.ts +1 -1
  3. package/dist/components/App.js +64 -53
  4. package/dist/components/BatteryDisplay.d.ts +8 -3
  5. package/dist/components/BatteryDisplay.js +7 -18
  6. package/dist/components/ConnectionStatus.d.ts +7 -2
  7. package/dist/components/ConnectionStatus.js +5 -4
  8. package/dist/components/Header.d.ts +7 -2
  9. package/dist/components/Header.js +11 -3
  10. package/dist/components/HelpDisplay.d.ts +5 -2
  11. package/dist/components/HelpDisplay.js +8 -3
  12. package/dist/components/HorizontalRule.d.ts +2 -0
  13. package/dist/components/HorizontalRule.js +7 -0
  14. package/dist/components/Panel.d.ts +18 -0
  15. package/dist/components/Panel.js +57 -0
  16. package/dist/components/Presets.d.ts +8 -3
  17. package/dist/components/Presets.js +13 -8
  18. package/dist/components/SettingsView.d.ts +9 -3
  19. package/dist/components/SettingsView.js +82 -16
  20. package/dist/components/TemperatureControl.d.ts +8 -3
  21. package/dist/components/TemperatureControl.js +13 -18
  22. package/dist/components/TemperatureDisplay.d.ts +8 -3
  23. package/dist/components/TemperatureDisplay.js +8 -35
  24. package/dist/hooks/useMug.d.ts +1 -1
  25. package/dist/hooks/useMug.js +22 -22
  26. package/dist/lib/bluetooth.d.ts +2 -0
  27. package/dist/lib/bluetooth.js +8 -0
  28. package/dist/lib/mock-bluetooth.d.ts +65 -0
  29. package/dist/lib/mock-bluetooth.js +214 -0
  30. package/dist/lib/settings.d.ts +1 -1
  31. package/dist/lib/settings.js +20 -20
  32. package/dist/lib/theme.d.ts +135 -0
  33. package/dist/lib/theme.js +112 -0
  34. package/dist/lib/types.d.ts +0 -1
  35. package/dist/lib/types.js +12 -12
  36. package/dist/lib/utils.d.ts +1 -2
  37. package/dist/lib/utils.js +19 -35
  38. package/package.json +2 -1
  39. package/dist/components/ColorControl.d.ts +0 -9
  40. package/dist/components/ColorControl.js +0 -71
package/dist/lib/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- import { TemperatureUnit, LiquidState, MIN_TEMP_CELSIUS, MAX_TEMP_CELSIUS, BATTERY_DRAIN_RATE_HEATING, BATTERY_DRAIN_RATE_MAINTAINING, BATTERY_CHARGE_RATE, } from './types.js';
1
+ import { TemperatureUnit, LiquidState, MIN_TEMP_CELSIUS, MAX_TEMP_CELSIUS, BATTERY_DRAIN_RATE_HEATING, BATTERY_DRAIN_RATE_MAINTAINING, BATTERY_CHARGE_RATE, } from "./types.js";
2
2
  export function formatTemperature(temp, unit) {
3
3
  if (unit === TemperatureUnit.Celsius) {
4
4
  return `${temp.toFixed(1)}°C`;
@@ -13,52 +13,36 @@ export function fahrenheitToCelsius(fahrenheit) {
13
13
  return ((fahrenheit - 32) * 5) / 9;
14
14
  }
15
15
  export function formatBatteryLevel(level) {
16
- return `${level}%`;
16
+ return `${Math.round(level)}%`;
17
17
  }
18
18
  export function getBatteryIcon(level, isCharging) {
19
19
  if (isCharging) {
20
- return '⚡';
20
+ return "~";
21
21
  }
22
22
  if (level >= 75)
23
- return '████';
23
+ return "||||";
24
24
  if (level >= 50)
25
- return '███░';
25
+ return "|||.";
26
26
  if (level >= 25)
27
- return '██░░';
27
+ return "||..";
28
28
  if (level >= 10)
29
- return '█░░░';
30
- return '░░░░';
29
+ return "|...";
30
+ return "....";
31
31
  }
32
32
  export function getLiquidStateText(state) {
33
33
  switch (state) {
34
34
  case LiquidState.Empty:
35
- return 'Empty';
35
+ return "Empty";
36
36
  case LiquidState.Filling:
37
- return 'Filling';
37
+ return "Filling";
38
38
  case LiquidState.Cooling:
39
- return 'Cooling';
39
+ return "Cooling";
40
40
  case LiquidState.Heating:
41
- return 'Heating';
41
+ return "Heating";
42
42
  case LiquidState.StableTemperature:
43
- return 'Perfect';
43
+ return "Perfect Temperature";
44
44
  default:
45
- return 'Unknown';
46
- }
47
- }
48
- export function getLiquidStateIcon(state) {
49
- switch (state) {
50
- case LiquidState.Empty:
51
- return '○';
52
- case LiquidState.Filling:
53
- return '◐';
54
- case LiquidState.Cooling:
55
- return '❄';
56
- case LiquidState.Heating:
57
- return '🔥';
58
- case LiquidState.StableTemperature:
59
- return '✓';
60
- default:
61
- return '?';
45
+ return "Unknown";
62
46
  }
63
47
  }
64
48
  export function clampTemperature(temp) {
@@ -66,7 +50,7 @@ export function clampTemperature(temp) {
66
50
  }
67
51
  export function formatDuration(minutes) {
68
52
  if (minutes < 1) {
69
- return '< 1 min';
53
+ return "< 1 min";
70
54
  }
71
55
  if (minutes < 60) {
72
56
  return `${Math.round(minutes)} min`;
@@ -122,12 +106,12 @@ export function estimateBatteryLife(batteryLevel, isCharging, liquidState) {
122
106
  export function getTemperatureColor(currentTemp, targetTemp) {
123
107
  const diff = currentTemp - targetTemp;
124
108
  if (Math.abs(diff) < 1) {
125
- return 'green'; // At target
109
+ return "green"; // At target
126
110
  }
127
111
  if (diff > 0) {
128
- return 'red'; // Too hot
112
+ return "red"; // Too hot
129
113
  }
130
- return 'blue'; // Too cold
114
+ return "blue"; // Too cold
131
115
  }
132
116
  export function interpolateColor(value, minColor, maxColor) {
133
117
  const clampedValue = Math.max(0, Math.min(1, value));
@@ -138,7 +122,7 @@ export function interpolateColor(value, minColor, maxColor) {
138
122
  ];
139
123
  }
140
124
  export function rgbToHex(r, g, b) {
141
- return `#${[r, g, b].map((c) => c.toString(16).padStart(2, '0')).join('')}`;
125
+ return `#${[r, g, b].map((c) => c.toString(16).padStart(2, "0")).join("")}`;
142
126
  }
143
127
  export function hexToRgb(hex) {
144
128
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember-mug",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "A CLI app for controlling Ember mugs via Bluetooth",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -19,6 +19,7 @@
19
19
  "prebuild": "npm run clean",
20
20
  "start": "node dist/cli.js",
21
21
  "dev": "tsx src/cli.tsx",
22
+ "dev-mocked": "EMBER_MOCK=true tsx src/cli.tsx",
22
23
  "prepublishOnly": "npm run build",
23
24
  "version": "npm run build"
24
25
  },
@@ -1,9 +0,0 @@
1
- import React from 'react';
2
- import { RGBColor } from '../lib/types.js';
3
- interface ColorControlProps {
4
- color: RGBColor;
5
- onColorChange: (color: RGBColor) => void;
6
- isActive: boolean;
7
- }
8
- export declare function ColorControl({ color, onColorChange, isActive, }: ColorControlProps): React.ReactElement;
9
- export {};
@@ -1,71 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { Box, Text, useInput } from 'ink';
4
- import { rgbToHex } from '../lib/utils.js';
5
- const PRESET_COLORS = [
6
- { name: 'Orange', color: { r: 255, g: 147, b: 41, a: 255 } },
7
- { name: 'Red', color: { r: 255, g: 0, b: 0, a: 255 } },
8
- { name: 'Green', color: { r: 0, g: 255, b: 0, a: 255 } },
9
- { name: 'Blue', color: { r: 0, g: 128, b: 255, a: 255 } },
10
- { name: 'Purple', color: { r: 128, g: 0, b: 255, a: 255 } },
11
- { name: 'Pink', color: { r: 255, g: 105, b: 180, a: 255 } },
12
- { name: 'White', color: { r: 255, g: 255, b: 255, a: 255 } },
13
- { name: 'Teal', color: { r: 0, g: 255, b: 200, a: 255 } },
14
- ];
15
- export function ColorControl({ color, onColorChange, isActive, }) {
16
- const [selectedChannel, setSelectedChannel] = useState('r');
17
- const [customMode, setCustomMode] = useState(false);
18
- useInput((input, key) => {
19
- if (!isActive)
20
- return;
21
- // Number keys for preset colors
22
- const numKey = parseInt(input, 10);
23
- if (numKey >= 1 && numKey <= PRESET_COLORS.length) {
24
- onColorChange(PRESET_COLORS[numKey - 1].color);
25
- setCustomMode(false);
26
- return;
27
- }
28
- // Toggle custom mode
29
- if (input === 'c') {
30
- setCustomMode(!customMode);
31
- return;
32
- }
33
- if (customMode) {
34
- // Switch channels
35
- if (input === 'r') {
36
- setSelectedChannel('r');
37
- }
38
- else if (input === 'g') {
39
- setSelectedChannel('g');
40
- }
41
- else if (input === 'b') {
42
- setSelectedChannel('b');
43
- }
44
- // Adjust selected channel
45
- let delta = 0;
46
- if (key.leftArrow || input === 'h') {
47
- delta = -10;
48
- }
49
- else if (key.rightArrow || input === 'l') {
50
- delta = 10;
51
- }
52
- else if (key.upArrow || input === 'k') {
53
- delta = 25;
54
- }
55
- else if (key.downArrow || input === 'j') {
56
- delta = -25;
57
- }
58
- if (delta !== 0) {
59
- const newColor = { ...color };
60
- newColor[selectedChannel] = Math.max(0, Math.min(255, newColor[selectedChannel] + delta));
61
- onColorChange(newColor);
62
- }
63
- }
64
- }, { isActive });
65
- return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Box, { justifyContent: "center", children: _jsx(Text, { bold: true, children: "LED Color" }) }), _jsx(Box, { justifyContent: "center", marginY: 1, children: _jsxs(Text, { children: ["Current: ", _jsx(Text, { color: rgbToHex(color.r, color.g, color.b), children: "\u25CF" }), ' ', _jsxs(Text, { dimColor: true, children: ["(", rgbToHex(color.r, color.g, color.b), ")"] })] }) }), _jsx(Box, { justifyContent: "center", gap: 1, marginY: 1, children: PRESET_COLORS.map((preset, index) => (_jsxs(Box, { children: [_jsx(Text, { color: rgbToHex(preset.color.r, preset.color.g, preset.color.b), children: "\u25CF" }), _jsx(Text, { dimColor: true, children: index + 1 })] }, preset.name))) }), customMode && (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Box, { justifyContent: "center", children: _jsx(Text, { dimColor: true, children: "Custom Color Mode" }) }), _jsxs(Box, { justifyContent: "center", gap: 2, children: [_jsx(ChannelSlider, { label: "R", value: color.r, isSelected: selectedChannel === 'r', color: "red" }), _jsx(ChannelSlider, { label: "G", value: color.g, isSelected: selectedChannel === 'g', color: "green" }), _jsx(ChannelSlider, { label: "B", value: color.b, isSelected: selectedChannel === 'b', color: "blue" })] })] })), _jsx(Box, { justifyContent: "center", marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Press ", _jsxs(Text, { color: "cyan", children: ["1-", PRESET_COLORS.length] }), " for presets |", ' ', _jsx(Text, { color: "cyan", children: "c" }), " for custom mode"] }) })] }));
66
- }
67
- function ChannelSlider({ label, value, isSelected, color, }) {
68
- const width = 10;
69
- const filled = Math.round((value / 255) * width);
70
- return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? color : 'gray', children: [label, ": ", '█'.repeat(filled), '░'.repeat(width - filled), " ", value.toString().padStart(3)] }) }));
71
- }