@mandujs/core 0.18.14 β†’ 0.18.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/core",
3
- "version": "0.18.14",
3
+ "version": "0.18.16",
4
4
  "description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -43,6 +43,19 @@ const baseStyles = `
43
43
  line-height: 1.5;
44
44
  }
45
45
 
46
+ button {
47
+ background: none;
48
+ border: none;
49
+ cursor: pointer;
50
+ font-family: inherit;
51
+ color: inherit;
52
+ font-size: inherit;
53
+ -webkit-appearance: none;
54
+ appearance: none;
55
+ padding: 0;
56
+ margin: 0;
57
+ }
58
+
46
59
  .mk-badge-container {
47
60
  position: fixed;
48
61
  z-index: ${zIndex.devtools};
@@ -72,6 +85,25 @@ const baseStyles = `
72
85
  .mk-badge-container.panel-open {
73
86
  opacity: 0;
74
87
  pointer-events: none;
88
+ transform: scale(0.8);
89
+ }
90
+
91
+ @keyframes mk-badge-breathe {
92
+ 0%, 100% { transform: scale(1) translateY(0px); }
93
+ 50% { transform: scale(1.04) translateY(0px); }
94
+ }
95
+
96
+ @keyframes mk-badge-attention {
97
+ 0%, 100% { transform: scale(1) translateY(0px); }
98
+ 15% { transform: scale(1.08) translateY(0px); }
99
+ 30% { transform: scale(0.98) translateY(0px); }
100
+ 45% { transform: scale(1.04) translateY(0px); }
101
+ 60% { transform: scale(1) translateY(0px); }
102
+ }
103
+
104
+ @keyframes mk-badge-float {
105
+ 0%, 100% { transform: scale(1) translateY(0px); }
106
+ 50% { transform: scale(1) translateY(-3px); }
75
107
  }
76
108
  `;
77
109
 
@@ -203,37 +203,78 @@ export function ManduBadge({
203
203
  const character = MANDU_CHARACTERS[state];
204
204
  const stateColor = stateColors[state];
205
205
  const [isHovered, setIsHovered] = React.useState(false);
206
+ const [isPressed, setIsPressed] = React.useState(false);
206
207
 
207
208
  const badgeStyle: React.CSSProperties = {
209
+ position: 'relative',
208
210
  display: 'flex',
209
211
  alignItems: 'center',
210
212
  justifyContent: 'center',
211
- gap: '8px',
212
- padding: count > 0 ? '10px 16px' : '0',
213
- width: count > 0 ? 'auto' : '48px',
214
- height: '48px',
213
+ width: '52px',
214
+ height: '52px',
215
+ padding: 0,
215
216
  borderRadius: '9999px',
216
- backgroundColor: colors.background.dark,
217
- border: `2px solid ${isHovered ? colors.brand.accent : stateColor}`,
217
+ backgroundColor: isPressed
218
+ ? colors.background.light
219
+ : isHovered
220
+ ? colors.background.medium
221
+ : colors.background.dark,
222
+ border: `2px solid ${isPressed ? stateColor : isHovered ? colors.brand.accent : stateColor + '70'}`,
218
223
  cursor: 'pointer',
219
- transition: `all ${animation.duration.normal} ${animation.easing.spring}`,
220
- boxShadow: isHovered
221
- ? `0 8px 24px rgba(8, 6, 18, 0.4), 0 0 0 4px ${stateColor}33`
222
- : `0 4px 12px rgba(8, 6, 18, 0.3)`,
223
- fontSize: '22px',
224
+ transition: `all 200ms ${animation.easing.spring}`,
225
+ boxShadow: isPressed
226
+ ? `0 2px 8px rgba(8, 6, 18, 0.4), inset 0 2px 4px rgba(0, 0, 0, 0.15)`
227
+ : isHovered
228
+ ? `0 8px 32px rgba(8, 6, 18, 0.45), 0 0 20px ${stateColor}55, 0 0 0 3px ${stateColor}20`
229
+ : `0 4px 16px rgba(8, 6, 18, 0.35), 0 0 10px ${stateColor}30`,
224
230
  userSelect: 'none',
225
- transform: isHovered ? 'scale(1.08)' : 'scale(1)',
231
+ transform: isPressed
232
+ ? 'scale(0.92) translateY(1px)'
233
+ : isHovered
234
+ ? 'scale(1.1) translateY(-2px)'
235
+ : 'scale(1) translateY(0px)',
226
236
  outline: 'none',
227
237
  lineHeight: 1,
238
+ animation: isHovered || isPressed
239
+ ? 'none'
240
+ : state === 'normal'
241
+ ? 'mk-badge-breathe 3s ease-in-out infinite'
242
+ : state === 'error' && count > 0
243
+ ? 'mk-badge-attention 2s ease-in-out infinite'
244
+ : state === 'loading'
245
+ ? 'mk-badge-float 2s ease-in-out infinite'
246
+ : 'none',
228
247
  };
229
248
 
230
- const countStyle: React.CSSProperties = {
231
- fontSize: '13px',
249
+ const emojiStyle: React.CSSProperties = {
250
+ fontFamily: "'Segoe UI Emoji', 'Apple Color Emoji', 'Noto Color Emoji', sans-serif",
251
+ color: colors.text.primary,
252
+ fontSize: '24px',
253
+ lineHeight: 1,
254
+ transition: `transform 200ms ${animation.easing.spring}`,
255
+ transform: isHovered ? 'rotate(-8deg)' : 'rotate(0deg)',
256
+ };
257
+
258
+ const countBubbleStyle: React.CSSProperties = {
259
+ position: 'absolute',
260
+ top: '-4px',
261
+ right: '-4px',
262
+ minWidth: '20px',
263
+ height: '20px',
264
+ padding: '0 5px',
265
+ borderRadius: '9999px',
266
+ backgroundColor: stateColor,
267
+ color: colors.background.dark,
268
+ fontSize: '11px',
232
269
  fontWeight: 700,
233
- color: stateColor,
234
- minWidth: '18px',
235
- textAlign: 'center',
270
+ display: 'flex',
271
+ alignItems: 'center',
272
+ justifyContent: 'center',
236
273
  lineHeight: 1,
274
+ boxShadow: `0 2px 8px ${stateColor}66`,
275
+ border: `2px solid ${colors.background.dark}`,
276
+ transition: `all 200ms ${animation.easing.spring}`,
277
+ transform: isHovered ? 'scale(1.15)' : 'scale(1)',
237
278
  };
238
279
 
239
280
  return (
@@ -242,13 +283,19 @@ export function ManduBadge({
242
283
  style={badgeStyle}
243
284
  onClick={onClick}
244
285
  onMouseEnter={() => setIsHovered(true)}
245
- onMouseLeave={() => setIsHovered(false)}
286
+ onMouseLeave={() => { setIsHovered(false); setIsPressed(false); }}
287
+ onMouseDown={() => setIsPressed(true)}
288
+ onMouseUp={() => setIsPressed(false)}
246
289
  onFocus={() => setIsHovered(true)}
247
- onBlur={() => setIsHovered(false)}
290
+ onBlur={() => { setIsHovered(false); setIsPressed(false); }}
248
291
  aria-label={`Mandu Kitchen: ${character.message}${count > 0 ? `, ${count} issues` : ''}`}
249
292
  >
250
- <span aria-hidden="true">πŸ₯Ÿ</span>
251
- {count > 0 && <span style={countStyle}>{count}</span>}
293
+ <span aria-hidden="true" style={emojiStyle}>πŸ₯Ÿ</span>
294
+ {count > 0 && (
295
+ <span style={countBubbleStyle}>
296
+ {count > 99 ? '99+' : count}
297
+ </span>
298
+ )}
252
299
  </button>
253
300
  );
254
301
  }
@@ -112,7 +112,7 @@ const styles = {
112
112
  tabActive: {
113
113
  color: colors.brand.accent,
114
114
  borderBottomColor: colors.brand.accent,
115
- backgroundColor: colors.background.medium,
115
+ backgroundColor: 'rgba(232, 150, 122, 0.12)',
116
116
  },
117
117
  tabIcon: {
118
118
  fontSize: typography.fontSize.md,
@@ -166,6 +166,8 @@ export function PanelContainer({
166
166
  }: PanelContainerProps): React.ReactElement {
167
167
  const [isResizing, setIsResizing] = useState(false);
168
168
  const [height, setHeight] = useState(400);
169
+ const [hoveredTab, setHoveredTab] = useState<TabId | null>(null);
170
+ const [isCloseHovered, setIsCloseHovered] = useState(false);
169
171
 
170
172
  const getTabBadgeCount = useCallback((tabId: TabId): number => {
171
173
  switch (tabId) {
@@ -206,8 +208,13 @@ export function PanelContainer({
206
208
  <span>Mandu Kitchen</span>
207
209
  </div>
208
210
  <button
209
- style={styles.closeButton}
211
+ style={{
212
+ ...styles.closeButton,
213
+ ...(isCloseHovered ? { color: colors.text.primary, backgroundColor: 'rgba(255, 255, 255, 0.08)' } : {}),
214
+ }}
210
215
  onClick={onClose}
216
+ onMouseEnter={() => setIsCloseHovered(true)}
217
+ onMouseLeave={() => setIsCloseHovered(false)}
211
218
  aria-label="νŒ¨λ„ λ‹«κΈ°"
212
219
  >
213
220
  Γ—
@@ -229,8 +236,14 @@ export function PanelContainer({
229
236
  style={{
230
237
  ...styles.tab,
231
238
  ...(isActive ? styles.tabActive : {}),
239
+ ...(!isActive && hoveredTab === tab.id ? {
240
+ color: colors.text.primary,
241
+ backgroundColor: 'rgba(255, 255, 255, 0.05)',
242
+ } : {}),
232
243
  }}
233
244
  onClick={() => onTabChange(tab.id)}
245
+ onMouseEnter={() => setHoveredTab(tab.id)}
246
+ onMouseLeave={() => setHoveredTab(null)}
234
247
  >
235
248
  <span style={styles.tabIcon}>{tab.icon}</span>
236
249
  <span>{tab.label}</span>