dhammawatthumpra commited on
Commit
789d216
·
1 Parent(s): 8ae844b

design(ai): enhance speed dial labels clarity and implement responsive positioning clamping

Browse files
webapp/tipitaka-web/src/components/ai/AIFloatingMenu.tsx CHANGED
@@ -133,7 +133,7 @@ const AIFloatingMenu: React.FC = () => {
133
  onClick={() => handleAction(act.prompt, act.isPrompt)}
134
  >
135
  {/* Label Pill */}
136
- <span className={`mr-3 px-3 py-1.5 rounded-xl text-[13px] md:text-sm font-bold border shadow-md whitespace-nowrap transition-transform duration-200 group-hover:scale-105 ${s.labelBg}`}>
137
  {act.label}
138
  </span>
139
 
 
133
  onClick={() => handleAction(act.prompt, act.isPrompt)}
134
  >
135
  {/* Label Pill */}
136
+ <span className={`mr-3 px-4 py-1.5 rounded-xl text-sm md:text-[15px] font-extrabold border shadow-md whitespace-nowrap transition-all duration-200 group-hover:scale-105 group-hover:border-[#0d9488] group-hover:text-[#0d9488] dark:group-hover:text-[#14b8a6] dark:group-hover:border-[#14b8a6] ${s.labelBg}`}>
137
  {act.label}
138
  </span>
139
 
webapp/tipitaka-web/src/components/ai/AIPopup.tsx CHANGED
@@ -52,27 +52,54 @@ const AIPopup: React.FC = () => {
52
 
53
  // Restore saved position + width/height on mount
54
  useEffect(() => {
55
- if (dragPos) setPos(dragPos);
56
-
57
  const savedW = localStorage.getItem('tipitaka-ai-panel-width');
58
  if (savedW) {
59
  const w = parseInt(savedW, 10);
60
  if (w >= 300 && w <= window.innerWidth * 0.95) {
 
61
  setWidth(w);
62
  setPanelWidth(w);
63
  }
64
  }
65
 
 
66
  const savedH = localStorage.getItem('tipitaka-ai-panel-height');
67
  if (savedH) {
68
  const h = parseInt(savedH, 10);
69
  if (h >= 200 && h <= window.innerHeight * 0.9) {
 
70
  setHeight(h);
71
  setPanelHeight(h);
72
  }
73
  }
 
 
 
 
 
 
74
  }, []);
75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
 
78
  const handleMouseDown = useCallback((e: React.MouseEvent) => {
 
52
 
53
  // Restore saved position + width/height on mount
54
  useEffect(() => {
55
+ let currentW = panelWidth;
 
56
  const savedW = localStorage.getItem('tipitaka-ai-panel-width');
57
  if (savedW) {
58
  const w = parseInt(savedW, 10);
59
  if (w >= 300 && w <= window.innerWidth * 0.95) {
60
+ currentW = w;
61
  setWidth(w);
62
  setPanelWidth(w);
63
  }
64
  }
65
 
66
+ let currentH = panelHeight;
67
  const savedH = localStorage.getItem('tipitaka-ai-panel-height');
68
  if (savedH) {
69
  const h = parseInt(savedH, 10);
70
  if (h >= 200 && h <= window.innerHeight * 0.9) {
71
+ currentH = h;
72
  setHeight(h);
73
  setPanelHeight(h);
74
  }
75
  }
76
+
77
+ if (dragPos) {
78
+ const clampedX = Math.max(0, Math.min(dragPos.x, window.innerWidth - currentW));
79
+ const clampedY = Math.max(0, Math.min(dragPos.y, window.innerHeight - currentH));
80
+ setPos({ x: clampedX, y: clampedY });
81
+ }
82
  }, []);
83
 
84
+ // Clamp position when window size changes to prevent window from going off-screen
85
+ useEffect(() => {
86
+ const handleResize = () => {
87
+ if (window.innerWidth < 768) {
88
+ setPos(null);
89
+ return;
90
+ }
91
+ setPos(prev => {
92
+ const targetPos = prev || dragPos;
93
+ if (!targetPos) return null;
94
+ const clampedX = Math.max(0, Math.min(targetPos.x, window.innerWidth - width));
95
+ const clampedY = Math.max(0, Math.min(targetPos.y, window.innerHeight - height));
96
+ return { x: clampedX, y: clampedY };
97
+ });
98
+ };
99
+ window.addEventListener('resize', handleResize);
100
+ return () => window.removeEventListener('resize', handleResize);
101
+ }, [width, height, dragPos]);
102
+
103
 
104
 
105
  const handleMouseDown = useCallback((e: React.MouseEvent) => {
webapp/tipitaka-web/src/stores/aiStore.ts CHANGED
@@ -45,7 +45,14 @@ export const useAIStore = create<AIState>((set, get) => ({
45
  ragReady: false,
46
  ragChecking: false,
47
  ragLoading: false,
48
- dragPos: null,
 
 
 
 
 
 
 
49
  viewMode: 'chat',
50
  panelWidth: 400,
51
  panelHeight: 550,
 
45
  ragReady: false,
46
  ragChecking: false,
47
  ragLoading: false,
48
+ dragPos: (() => {
49
+ try {
50
+ const pos = localStorage.getItem('tipitaka-ai-drag-pos');
51
+ return pos ? JSON.parse(pos) : null;
52
+ } catch {
53
+ return null;
54
+ }
55
+ })(),
56
  viewMode: 'chat',
57
  panelWidth: 400,
58
  panelHeight: 550,