Spaces:
Sleeping
Sleeping
FIx window dragging and opening
Browse files- .claude/settings.local.json +5 -1
- app/components/Calendar.tsx +5 -1
- app/components/Clock.tsx +5 -1
- app/components/Desktop.tsx +221 -220
- app/components/Dock.tsx +3 -1
- app/components/Window.tsx +29 -12
.claude/settings.local.json
CHANGED
|
@@ -22,7 +22,11 @@
|
|
| 22 |
"Bash(git push:*)",
|
| 23 |
"Bash(git filter-branch:*)",
|
| 24 |
"Bash(git ls-tree:*)",
|
| 25 |
-
"Bash(tree:*)"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
],
|
| 27 |
"deny": [],
|
| 28 |
"ask": []
|
|
|
|
| 22 |
"Bash(git push:*)",
|
| 23 |
"Bash(git filter-branch:*)",
|
| 24 |
"Bash(git ls-tree:*)",
|
| 25 |
+
"Bash(tree:*)",
|
| 26 |
+
"mcp__puppeteer__puppeteer_navigate",
|
| 27 |
+
"mcp__puppeteer__puppeteer_screenshot",
|
| 28 |
+
"mcp__puppeteer__puppeteer_click",
|
| 29 |
+
"mcp__puppeteer__puppeteer_evaluate"
|
| 30 |
],
|
| 31 |
"deny": [],
|
| 32 |
"ask": []
|
app/components/Calendar.tsx
CHANGED
|
@@ -19,6 +19,8 @@ interface CalendarProps {
|
|
| 19 |
onClose: () => void
|
| 20 |
onMinimize?: () => void
|
| 21 |
onMaximize?: () => void
|
|
|
|
|
|
|
| 22 |
}
|
| 23 |
|
| 24 |
interface CalendarEvent extends Holiday {
|
|
@@ -27,7 +29,7 @@ interface CalendarEvent extends Holiday {
|
|
| 27 |
|
| 28 |
type ViewMode = 'day' | 'week' | 'month' | 'year'
|
| 29 |
|
| 30 |
-
export function Calendar({ onClose, onMinimize, onMaximize }: CalendarProps) {
|
| 31 |
const [currentDate, setCurrentDate] = useState(new Date())
|
| 32 |
const [view, setView] = useState<ViewMode>('month')
|
| 33 |
const [selectedDate, setSelectedDate] = useState<Date | null>(null)
|
|
@@ -205,6 +207,8 @@ export function Calendar({ onClose, onMinimize, onMaximize }: CalendarProps) {
|
|
| 205 |
onClose={onClose}
|
| 206 |
onMinimize={onMinimize}
|
| 207 |
onMaximize={onMaximize}
|
|
|
|
|
|
|
| 208 |
width={1100}
|
| 209 |
height={750}
|
| 210 |
x={50}
|
|
|
|
| 19 |
onClose: () => void
|
| 20 |
onMinimize?: () => void
|
| 21 |
onMaximize?: () => void
|
| 22 |
+
onFocus?: () => void
|
| 23 |
+
zIndex?: number
|
| 24 |
}
|
| 25 |
|
| 26 |
interface CalendarEvent extends Holiday {
|
|
|
|
| 29 |
|
| 30 |
type ViewMode = 'day' | 'week' | 'month' | 'year'
|
| 31 |
|
| 32 |
+
export function Calendar({ onClose, onMinimize, onMaximize, onFocus, zIndex }: CalendarProps) {
|
| 33 |
const [currentDate, setCurrentDate] = useState(new Date())
|
| 34 |
const [view, setView] = useState<ViewMode>('month')
|
| 35 |
const [selectedDate, setSelectedDate] = useState<Date | null>(null)
|
|
|
|
| 207 |
onClose={onClose}
|
| 208 |
onMinimize={onMinimize}
|
| 209 |
onMaximize={onMaximize}
|
| 210 |
+
onFocus={onFocus}
|
| 211 |
+
zIndex={zIndex}
|
| 212 |
width={1100}
|
| 213 |
height={750}
|
| 214 |
x={50}
|
app/components/Clock.tsx
CHANGED
|
@@ -7,9 +7,11 @@ interface ClockProps {
|
|
| 7 |
onClose: () => void
|
| 8 |
onMinimize?: () => void
|
| 9 |
onMaximize?: () => void
|
|
|
|
|
|
|
| 10 |
}
|
| 11 |
|
| 12 |
-
export function Clock({ onClose, onMinimize, onMaximize }: ClockProps) {
|
| 13 |
const [time, setTime] = useState(new Date())
|
| 14 |
const [viewMode, setViewMode] = useState<'analog' | 'digital' | 'world'>('analog')
|
| 15 |
|
|
@@ -51,6 +53,8 @@ export function Clock({ onClose, onMinimize, onMaximize }: ClockProps) {
|
|
| 51 |
onClose={onClose}
|
| 52 |
onMinimize={onMinimize}
|
| 53 |
onMaximize={onMaximize}
|
|
|
|
|
|
|
| 54 |
width={380}
|
| 55 |
height={480}
|
| 56 |
x={window.innerWidth / 2 - 190}
|
|
|
|
| 7 |
onClose: () => void
|
| 8 |
onMinimize?: () => void
|
| 9 |
onMaximize?: () => void
|
| 10 |
+
onFocus?: () => void
|
| 11 |
+
zIndex?: number
|
| 12 |
}
|
| 13 |
|
| 14 |
+
export function Clock({ onClose, onMinimize, onMaximize, onFocus, zIndex }: ClockProps) {
|
| 15 |
const [time, setTime] = useState(new Date())
|
| 16 |
const [viewMode, setViewMode] = useState<'analog' | 'digital' | 'world'>('analog')
|
| 17 |
|
|
|
|
| 53 |
onClose={onClose}
|
| 54 |
onMinimize={onMinimize}
|
| 55 |
onMaximize={onMaximize}
|
| 56 |
+
onFocus={onFocus}
|
| 57 |
+
zIndex={zIndex}
|
| 58 |
width={380}
|
| 59 |
height={480}
|
| 60 |
x={window.innerWidth / 2 - 190}
|
app/components/Desktop.tsx
CHANGED
|
@@ -82,12 +82,53 @@ export function Desktop() {
|
|
| 82 |
const [latexEditorMinimized, setLaTeXEditorMinimized] = useState(false)
|
| 83 |
|
| 84 |
const [powerState, setPowerState] = useState<'active' | 'sleep' | 'restart' | 'shutdown'>('active')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
| 86 |
const openFileManager = (path: string) => {
|
| 87 |
console.log('Opening File Manager with path:', path)
|
| 88 |
setCurrentPath(path)
|
| 89 |
setFileManagerOpen(true)
|
| 90 |
setFileManagerMinimized(false)
|
|
|
|
| 91 |
}
|
| 92 |
|
| 93 |
const closeFileManager = () => {
|
|
@@ -99,6 +140,7 @@ export function Desktop() {
|
|
| 99 |
console.log('Opening Calendar')
|
| 100 |
setCalendarOpen(true)
|
| 101 |
setCalendarMinimized(false)
|
|
|
|
| 102 |
}
|
| 103 |
|
| 104 |
const closeCalendar = () => {
|
|
@@ -109,6 +151,7 @@ export function Desktop() {
|
|
| 109 |
const openClock = () => {
|
| 110 |
setClockOpen(true)
|
| 111 |
setClockMinimized(false)
|
|
|
|
| 112 |
}
|
| 113 |
|
| 114 |
const closeClock = () => {
|
|
@@ -122,6 +165,7 @@ export function Desktop() {
|
|
| 122 |
console.log('Opening Gemini Chat')
|
| 123 |
setGeminiChatOpen(true)
|
| 124 |
setGeminiChatMinimized(false)
|
|
|
|
| 125 |
}
|
| 126 |
|
| 127 |
const closeGeminiChat = () => {
|
|
@@ -134,6 +178,7 @@ export function Desktop() {
|
|
| 134 |
const openSessionManager = () => {
|
| 135 |
setSessionManagerOpen(true)
|
| 136 |
setSessionManagerMinimized(false)
|
|
|
|
| 137 |
}
|
| 138 |
|
| 139 |
const closeSessionManager = () => {
|
|
@@ -145,6 +190,7 @@ export function Desktop() {
|
|
| 145 |
setActiveFlutterApp(appFile)
|
| 146 |
setFlutterRunnerOpen(true)
|
| 147 |
setFlutterRunnerMinimized(false)
|
|
|
|
| 148 |
}
|
| 149 |
|
| 150 |
const closeFlutterRunner = () => {
|
|
@@ -156,6 +202,7 @@ export function Desktop() {
|
|
| 156 |
const openResearchBrowser = () => {
|
| 157 |
setResearchBrowserOpen(true)
|
| 158 |
setResearchBrowserMinimized(false)
|
|
|
|
| 159 |
}
|
| 160 |
|
| 161 |
const closeResearchBrowser = () => {
|
|
@@ -166,6 +213,7 @@ export function Desktop() {
|
|
| 166 |
const openFlutterCodeEditor = () => {
|
| 167 |
setFlutterCodeEditorOpen(true)
|
| 168 |
setFlutterCodeEditorMinimized(false)
|
|
|
|
| 169 |
}
|
| 170 |
|
| 171 |
const closeFlutterCodeEditor = () => {
|
|
@@ -176,6 +224,7 @@ export function Desktop() {
|
|
| 176 |
const openLaTeXEditor = () => {
|
| 177 |
setLaTeXEditorOpen(true)
|
| 178 |
setLaTeXEditorMinimized(false)
|
|
|
|
| 179 |
}
|
| 180 |
|
| 181 |
const closeLaTeXEditor = () => {
|
|
@@ -566,6 +615,7 @@ export function Desktop() {
|
|
| 566 |
onOpenCalendar={openCalendar}
|
| 567 |
onOpenClock={openClock}
|
| 568 |
onOpenGeminiChat={openGeminiChat}
|
|
|
|
| 569 |
openApps={{
|
| 570 |
files: fileManagerOpen,
|
| 571 |
calendar: calendarOpen,
|
|
@@ -609,6 +659,7 @@ export function Desktop() {
|
|
| 609 |
onDoubleClick={openClock}
|
| 610 |
/>
|
| 611 |
</div>
|
|
|
|
| 612 |
<div className="pointer-events-auto w-24 h-24">
|
| 613 |
<DraggableDesktopIcon
|
| 614 |
id="calendar"
|
|
@@ -630,6 +681,7 @@ export function Desktop() {
|
|
| 630 |
onDoubleClick={() => openFileManager('')}
|
| 631 |
/>
|
| 632 |
</div>
|
|
|
|
| 633 |
<div className="pointer-events-auto w-24 h-24">
|
| 634 |
<DraggableDesktopIcon
|
| 635 |
id="sessions"
|
|
@@ -640,6 +692,7 @@ export function Desktop() {
|
|
| 640 |
onDoubleClick={openSessionManager}
|
| 641 |
/>
|
| 642 |
</div>
|
|
|
|
| 643 |
<div className="pointer-events-auto w-24 h-24">
|
| 644 |
<DraggableDesktopIcon
|
| 645 |
id="research"
|
|
@@ -650,6 +703,7 @@ export function Desktop() {
|
|
| 650 |
onDoubleClick={openResearchBrowser}
|
| 651 |
/>
|
| 652 |
</div>
|
|
|
|
| 653 |
<div className="pointer-events-auto w-24 h-24">
|
| 654 |
<DraggableDesktopIcon
|
| 655 |
id="flutter-editor"
|
|
@@ -660,6 +714,7 @@ export function Desktop() {
|
|
| 660 |
onDoubleClick={openFlutterCodeEditor}
|
| 661 |
/>
|
| 662 |
</div>
|
|
|
|
| 663 |
<div className="pointer-events-auto w-24 h-24">
|
| 664 |
<DraggableDesktopIcon
|
| 665 |
id="latex-editor"
|
|
@@ -672,127 +727,132 @@ export function Desktop() {
|
|
| 672 |
</div>
|
| 673 |
</div>
|
| 674 |
|
| 675 |
-
|
| 676 |
-
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
| 680 |
-
|
| 681 |
-
|
| 682 |
-
|
| 683 |
-
|
| 684 |
-
|
| 685 |
-
|
| 686 |
-
|
| 687 |
-
opacity: 0,
|
| 688 |
-
|
| 689 |
-
|
| 690 |
-
|
| 691 |
-
|
| 692 |
-
|
| 693 |
-
|
| 694 |
-
|
| 695 |
-
transformOrigin: 'bottom center',
|
| 696 |
-
pointerEvents: 'auto',
|
| 697 |
-
zIndex: 1000
|
| 698 |
-
}}
|
| 699 |
-
>
|
| 700 |
-
<FileManager
|
| 701 |
currentPath={currentPath}
|
| 702 |
onNavigate={setCurrentPath}
|
| 703 |
onClose={closeFileManager}
|
| 704 |
onOpenFlutterApp={openFlutterRunner}
|
| 705 |
-
|
| 706 |
-
|
| 707 |
-
|
| 708 |
-
|
| 709 |
-
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
|
| 713 |
-
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
| 718 |
-
|
| 719 |
-
|
| 720 |
-
|
| 721 |
-
|
| 722 |
-
|
| 723 |
-
opacity: 0,
|
| 724 |
-
|
| 725 |
-
|
| 726 |
-
|
| 727 |
-
|
| 728 |
-
|
| 729 |
-
|
| 730 |
-
|
| 731 |
-
|
| 732 |
-
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
| 736 |
-
<
|
| 737 |
-
|
| 738 |
-
|
| 739 |
-
|
| 740 |
-
|
| 741 |
-
|
| 742 |
-
|
| 743 |
-
|
| 744 |
-
|
| 745 |
-
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
|
| 749 |
-
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
|
| 764 |
-
|
| 765 |
-
|
| 766 |
-
|
| 767 |
-
|
| 768 |
-
|
| 769 |
-
|
| 770 |
-
|
| 771 |
-
|
| 772 |
-
|
| 773 |
-
|
| 774 |
-
|
| 775 |
-
|
| 776 |
-
|
| 777 |
-
|
| 778 |
-
|
| 779 |
-
|
| 780 |
-
|
| 781 |
-
|
| 782 |
-
|
| 783 |
-
|
| 784 |
-
|
| 785 |
-
|
| 786 |
-
|
| 787 |
-
|
| 788 |
-
|
| 789 |
-
|
| 790 |
-
|
| 791 |
-
|
| 792 |
-
|
| 793 |
-
|
| 794 |
-
|
| 795 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 796 |
<SessionManagerWindow
|
| 797 |
onClose={closeSessionManager}
|
| 798 |
sessionId={userSession}
|
|
@@ -802,30 +862,22 @@ export function Desktop() {
|
|
| 802 |
</motion.div>
|
| 803 |
)}
|
| 804 |
|
| 805 |
-
|
| 806 |
-
|
| 807 |
-
|
| 808 |
-
|
| 809 |
-
|
| 810 |
-
|
| 811 |
-
|
| 812 |
-
|
| 813 |
-
|
| 814 |
-
|
| 815 |
-
|
| 816 |
-
|
| 817 |
-
|
| 818 |
-
|
| 819 |
-
|
| 820 |
-
|
| 821 |
-
transition={{ duration: 0.3, ease: "easeOut" }}
|
| 822 |
-
className="absolute"
|
| 823 |
-
style={{
|
| 824 |
-
transformOrigin: 'bottom center',
|
| 825 |
-
pointerEvents: 'auto',
|
| 826 |
-
zIndex: 1000
|
| 827 |
-
}}
|
| 828 |
-
>
|
| 829 |
<FlutterRunner
|
| 830 |
initialCode={activeFlutterApp?.dartCode}
|
| 831 |
sessionId={userSession}
|
|
@@ -838,28 +890,20 @@ export function Desktop() {
|
|
| 838 |
</motion.div>
|
| 839 |
)}
|
| 840 |
|
| 841 |
-
{researchBrowserOpen &&
|
| 842 |
<motion.div
|
| 843 |
key="research-browser"
|
| 844 |
-
initial={{
|
| 845 |
animate={{
|
| 846 |
-
|
| 847 |
-
|
| 848 |
-
|
| 849 |
-
}}
|
| 850 |
-
exit={{
|
| 851 |
-
scale: 0.2,
|
| 852 |
-
opacity: 0,
|
| 853 |
-
y: "80vh",
|
| 854 |
-
clipPath: "polygon(45% 100%, 55% 100%, 60% 0%, 40% 0%)",
|
| 855 |
-
transition: { duration: 0.6, ease: [0.4, 0, 0.2, 1] }
|
| 856 |
}}
|
| 857 |
-
|
| 858 |
-
|
| 859 |
style={{
|
| 860 |
-
|
| 861 |
-
|
| 862 |
-
zIndex: 1000
|
| 863 |
}}
|
| 864 |
>
|
| 865 |
<ResearchBrowser
|
|
@@ -869,90 +913,47 @@ export function Desktop() {
|
|
| 869 |
</motion.div>
|
| 870 |
)}
|
| 871 |
|
| 872 |
-
{flutterCodeEditorOpen &&
|
| 873 |
<motion.div
|
| 874 |
key="flutter-code-editor"
|
| 875 |
-
initial={{
|
| 876 |
animate={{
|
| 877 |
-
|
| 878 |
-
|
| 879 |
-
|
| 880 |
}}
|
| 881 |
-
exit={{
|
| 882 |
-
|
| 883 |
-
opacity: 0,
|
| 884 |
-
y: "80vh",
|
| 885 |
-
clipPath: "polygon(45% 100%, 55% 100%, 60% 0%, 40% 0%)",
|
| 886 |
-
transition: { duration: 0.6, ease: [0.4, 0, 0.2, 1] }
|
| 887 |
-
}}
|
| 888 |
-
transition={{ duration: 0.3, ease: "easeOut" }}
|
| 889 |
-
className="absolute"
|
| 890 |
style={{
|
| 891 |
-
|
| 892 |
-
|
| 893 |
-
zIndex: 1000
|
| 894 |
}}
|
| 895 |
>
|
| 896 |
<FlutterCodeEditor onClose={closeFlutterCodeEditor} onMinimize={() => setFlutterCodeEditorMinimized(true)} />
|
| 897 |
</motion.div>
|
| 898 |
)}
|
| 899 |
|
| 900 |
-
{latexEditorOpen &&
|
| 901 |
<motion.div
|
| 902 |
key="latex-editor"
|
| 903 |
-
initial={{
|
| 904 |
animate={{
|
| 905 |
-
|
| 906 |
-
|
| 907 |
-
|
| 908 |
-
}}
|
| 909 |
-
exit={{
|
| 910 |
-
scale: 0.2,
|
| 911 |
-
opacity: 0,
|
| 912 |
-
y: "80vh",
|
| 913 |
-
clipPath: "polygon(45% 100%, 55% 100%, 60% 0%, 40% 0%)",
|
| 914 |
-
transition: { duration: 0.6, ease: [0.4, 0, 0.2, 1] }
|
| 915 |
}}
|
| 916 |
-
|
| 917 |
-
|
| 918 |
style={{
|
| 919 |
-
|
| 920 |
-
|
| 921 |
-
zIndex: 1000
|
| 922 |
}}
|
| 923 |
>
|
| 924 |
<LaTeXEditor onClose={closeLaTeXEditor} sessionId={userSession} onMinimize={() => setLaTeXEditorMinimized(true)} />
|
| 925 |
</motion.div>
|
| 926 |
)}
|
| 927 |
-
{geminiChatOpen && !geminiChatMinimized && (
|
| 928 |
-
<motion.div
|
| 929 |
-
key="gemini"
|
| 930 |
-
initial={{ scale: 0.9, opacity: 0 }}
|
| 931 |
-
animate={{
|
| 932 |
-
scale: 1,
|
| 933 |
-
opacity: 1,
|
| 934 |
-
clipPath: "polygon(0 0, 100% 0, 100% 100%, 0% 100%)",
|
| 935 |
-
}}
|
| 936 |
-
exit={{
|
| 937 |
-
scale: 0.2,
|
| 938 |
-
opacity: 0,
|
| 939 |
-
y: "80vh",
|
| 940 |
-
clipPath: "polygon(45% 100%, 55% 100%, 60% 0%, 40% 0%)",
|
| 941 |
-
transition: { duration: 0.6, ease: [0.4, 0, 0.2, 1] }
|
| 942 |
-
}}
|
| 943 |
-
transition={{ duration: 0.3, ease: "easeOut" }}
|
| 944 |
-
className="absolute"
|
| 945 |
-
style={{
|
| 946 |
-
transformOrigin: 'bottom center',
|
| 947 |
-
pointerEvents: 'auto',
|
| 948 |
-
zIndex: 1000
|
| 949 |
-
}}
|
| 950 |
-
>
|
| 951 |
-
<GeminiChat onClose={closeGeminiChat} onMinimize={() => setGeminiChatMinimized(true)} />
|
| 952 |
-
</motion.div>
|
| 953 |
-
)}
|
| 954 |
</AnimatePresence>
|
| 955 |
-
|
| 956 |
</div>
|
| 957 |
|
| 958 |
{/* Spotlight Search */}
|
|
|
|
| 82 |
const [latexEditorMinimized, setLaTeXEditorMinimized] = useState(false)
|
| 83 |
|
| 84 |
const [powerState, setPowerState] = useState<'active' | 'sleep' | 'restart' | 'shutdown'>('active')
|
| 85 |
+
const [globalZIndex, setGlobalZIndex] = useState(1000)
|
| 86 |
+
const [windowZIndices, setWindowZIndices] = useState<{[key: string]: number}>({})
|
| 87 |
+
|
| 88 |
+
const getNextZIndex = () => {
|
| 89 |
+
const nextZ = globalZIndex + 1
|
| 90 |
+
setGlobalZIndex(nextZ)
|
| 91 |
+
return nextZ
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
const bringWindowToFront = (windowId: string) => {
|
| 95 |
+
setWindowZIndices(prev => ({ ...prev, [windowId]: getNextZIndex() }))
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
const closeAllApps = () => {
|
| 99 |
+
// Close all apps
|
| 100 |
+
setFileManagerOpen(false)
|
| 101 |
+
setCalendarOpen(false)
|
| 102 |
+
setClockOpen(false)
|
| 103 |
+
setGeminiChatOpen(false)
|
| 104 |
+
setSessionManagerOpen(false)
|
| 105 |
+
setFlutterRunnerOpen(false)
|
| 106 |
+
setResearchBrowserOpen(false)
|
| 107 |
+
setFlutterCodeEditorOpen(false)
|
| 108 |
+
setLaTeXEditorOpen(false)
|
| 109 |
+
|
| 110 |
+
// Reset all minimized states
|
| 111 |
+
setFileManagerMinimized(false)
|
| 112 |
+
setCalendarMinimized(false)
|
| 113 |
+
setClockMinimized(false)
|
| 114 |
+
setGeminiChatMinimized(false)
|
| 115 |
+
setSessionManagerMinimized(false)
|
| 116 |
+
setFlutterRunnerMinimized(false)
|
| 117 |
+
setResearchBrowserMinimized(false)
|
| 118 |
+
setFlutterCodeEditorMinimized(false)
|
| 119 |
+
setLaTeXEditorMinimized(false)
|
| 120 |
+
|
| 121 |
+
// Reset window z-indices
|
| 122 |
+
setWindowZIndices({})
|
| 123 |
+
setGlobalZIndex(1000)
|
| 124 |
+
}
|
| 125 |
|
| 126 |
const openFileManager = (path: string) => {
|
| 127 |
console.log('Opening File Manager with path:', path)
|
| 128 |
setCurrentPath(path)
|
| 129 |
setFileManagerOpen(true)
|
| 130 |
setFileManagerMinimized(false)
|
| 131 |
+
setWindowZIndices(prev => ({ ...prev, fileManager: getNextZIndex() }))
|
| 132 |
}
|
| 133 |
|
| 134 |
const closeFileManager = () => {
|
|
|
|
| 140 |
console.log('Opening Calendar')
|
| 141 |
setCalendarOpen(true)
|
| 142 |
setCalendarMinimized(false)
|
| 143 |
+
setWindowZIndices(prev => ({ ...prev, calendar: getNextZIndex() }))
|
| 144 |
}
|
| 145 |
|
| 146 |
const closeCalendar = () => {
|
|
|
|
| 151 |
const openClock = () => {
|
| 152 |
setClockOpen(true)
|
| 153 |
setClockMinimized(false)
|
| 154 |
+
setWindowZIndices(prev => ({ ...prev, clock: getNextZIndex() }))
|
| 155 |
}
|
| 156 |
|
| 157 |
const closeClock = () => {
|
|
|
|
| 165 |
console.log('Opening Gemini Chat')
|
| 166 |
setGeminiChatOpen(true)
|
| 167 |
setGeminiChatMinimized(false)
|
| 168 |
+
setWindowZIndices(prev => ({ ...prev, gemini: getNextZIndex() }))
|
| 169 |
}
|
| 170 |
|
| 171 |
const closeGeminiChat = () => {
|
|
|
|
| 178 |
const openSessionManager = () => {
|
| 179 |
setSessionManagerOpen(true)
|
| 180 |
setSessionManagerMinimized(false)
|
| 181 |
+
setWindowZIndices(prev => ({ ...prev, sessionManager: getNextZIndex() }))
|
| 182 |
}
|
| 183 |
|
| 184 |
const closeSessionManager = () => {
|
|
|
|
| 190 |
setActiveFlutterApp(appFile)
|
| 191 |
setFlutterRunnerOpen(true)
|
| 192 |
setFlutterRunnerMinimized(false)
|
| 193 |
+
setWindowZIndices(prev => ({ ...prev, flutterRunner: getNextZIndex() }))
|
| 194 |
}
|
| 195 |
|
| 196 |
const closeFlutterRunner = () => {
|
|
|
|
| 202 |
const openResearchBrowser = () => {
|
| 203 |
setResearchBrowserOpen(true)
|
| 204 |
setResearchBrowserMinimized(false)
|
| 205 |
+
setWindowZIndices(prev => ({ ...prev, researchBrowser: getNextZIndex() }))
|
| 206 |
}
|
| 207 |
|
| 208 |
const closeResearchBrowser = () => {
|
|
|
|
| 213 |
const openFlutterCodeEditor = () => {
|
| 214 |
setFlutterCodeEditorOpen(true)
|
| 215 |
setFlutterCodeEditorMinimized(false)
|
| 216 |
+
setWindowZIndices(prev => ({ ...prev, flutterCodeEditor: getNextZIndex() }))
|
| 217 |
}
|
| 218 |
|
| 219 |
const closeFlutterCodeEditor = () => {
|
|
|
|
| 224 |
const openLaTeXEditor = () => {
|
| 225 |
setLaTeXEditorOpen(true)
|
| 226 |
setLaTeXEditorMinimized(false)
|
| 227 |
+
setWindowZIndices(prev => ({ ...prev, latexEditor: getNextZIndex() }))
|
| 228 |
}
|
| 229 |
|
| 230 |
const closeLaTeXEditor = () => {
|
|
|
|
| 615 |
onOpenCalendar={openCalendar}
|
| 616 |
onOpenClock={openClock}
|
| 617 |
onOpenGeminiChat={openGeminiChat}
|
| 618 |
+
onCloseAllApps={closeAllApps}
|
| 619 |
openApps={{
|
| 620 |
files: fileManagerOpen,
|
| 621 |
calendar: calendarOpen,
|
|
|
|
| 659 |
onDoubleClick={openClock}
|
| 660 |
/>
|
| 661 |
</div>
|
| 662 |
+
|
| 663 |
<div className="pointer-events-auto w-24 h-24">
|
| 664 |
<DraggableDesktopIcon
|
| 665 |
id="calendar"
|
|
|
|
| 681 |
onDoubleClick={() => openFileManager('')}
|
| 682 |
/>
|
| 683 |
</div>
|
| 684 |
+
|
| 685 |
<div className="pointer-events-auto w-24 h-24">
|
| 686 |
<DraggableDesktopIcon
|
| 687 |
id="sessions"
|
|
|
|
| 692 |
onDoubleClick={openSessionManager}
|
| 693 |
/>
|
| 694 |
</div>
|
| 695 |
+
|
| 696 |
<div className="pointer-events-auto w-24 h-24">
|
| 697 |
<DraggableDesktopIcon
|
| 698 |
id="research"
|
|
|
|
| 703 |
onDoubleClick={openResearchBrowser}
|
| 704 |
/>
|
| 705 |
</div>
|
| 706 |
+
|
| 707 |
<div className="pointer-events-auto w-24 h-24">
|
| 708 |
<DraggableDesktopIcon
|
| 709 |
id="flutter-editor"
|
|
|
|
| 714 |
onDoubleClick={openFlutterCodeEditor}
|
| 715 |
/>
|
| 716 |
</div>
|
| 717 |
+
|
| 718 |
<div className="pointer-events-auto w-24 h-24">
|
| 719 |
<DraggableDesktopIcon
|
| 720 |
id="latex-editor"
|
|
|
|
| 727 |
</div>
|
| 728 |
</div>
|
| 729 |
|
| 730 |
+
{/* Windows Container */}
|
| 731 |
+
<div className="fixed inset-0 top-8 pointer-events-none" style={{ position: 'fixed', top: '32px', left: 0, right: 0, bottom: 0 }}>
|
| 732 |
+
<AnimatePresence>
|
| 733 |
+
{fileManagerOpen && (
|
| 734 |
+
<motion.div
|
| 735 |
+
key="file-manager"
|
| 736 |
+
initial={{ opacity: 0, scale: 0.95 }}
|
| 737 |
+
animate={{
|
| 738 |
+
opacity: fileManagerMinimized ? 0 : 1,
|
| 739 |
+
scale: fileManagerMinimized ? 0.9 : 1,
|
| 740 |
+
y: fileManagerMinimized ? 100 : 0,
|
| 741 |
+
}}
|
| 742 |
+
exit={{ opacity: 0, scale: 0.95 }}
|
| 743 |
+
transition={{ duration: 0.2 }}
|
| 744 |
+
style={{
|
| 745 |
+
pointerEvents: fileManagerMinimized ? 'none' : 'auto',
|
| 746 |
+
display: fileManagerMinimized ? 'none' : 'block'
|
| 747 |
+
}}
|
| 748 |
+
>
|
| 749 |
+
<FileManager
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 750 |
currentPath={currentPath}
|
| 751 |
onNavigate={setCurrentPath}
|
| 752 |
onClose={closeFileManager}
|
| 753 |
onOpenFlutterApp={openFlutterRunner}
|
| 754 |
+
onMinimize={() => setFileManagerMinimized(true)}
|
| 755 |
+
onFocus={() => bringWindowToFront('fileManager')}
|
| 756 |
+
zIndex={windowZIndices.fileManager || 1000}
|
| 757 |
+
sessionId={userSession}
|
| 758 |
+
onOpenApp={handleOpenApp}
|
| 759 |
+
/>
|
| 760 |
+
</motion.div>
|
| 761 |
+
)}
|
| 762 |
+
|
| 763 |
+
{calendarOpen && (
|
| 764 |
+
<motion.div
|
| 765 |
+
key="calendar"
|
| 766 |
+
initial={{ opacity: 0, scale: 0.95 }}
|
| 767 |
+
animate={{
|
| 768 |
+
opacity: calendarMinimized ? 0 : 1,
|
| 769 |
+
scale: calendarMinimized ? 0.9 : 1,
|
| 770 |
+
y: calendarMinimized ? 100 : 0,
|
| 771 |
+
}}
|
| 772 |
+
exit={{ opacity: 0, scale: 0.95 }}
|
| 773 |
+
transition={{ duration: 0.2 }}
|
| 774 |
+
style={{
|
| 775 |
+
pointerEvents: calendarMinimized ? 'none' : 'auto',
|
| 776 |
+
display: calendarMinimized ? 'none' : 'block'
|
| 777 |
+
}}
|
| 778 |
+
>
|
| 779 |
+
<Calendar
|
| 780 |
+
onClose={closeCalendar}
|
| 781 |
+
onMinimize={() => setCalendarMinimized(true)}
|
| 782 |
+
onFocus={() => bringWindowToFront('calendar')}
|
| 783 |
+
zIndex={windowZIndices.calendar || 1000}
|
| 784 |
+
/>
|
| 785 |
+
</motion.div>
|
| 786 |
+
)}
|
| 787 |
+
|
| 788 |
+
{clockOpen && (
|
| 789 |
+
<motion.div
|
| 790 |
+
key="clock"
|
| 791 |
+
initial={{ opacity: 0, scale: 0.95 }}
|
| 792 |
+
animate={{
|
| 793 |
+
opacity: clockMinimized ? 0 : 1,
|
| 794 |
+
scale: clockMinimized ? 0.9 : 1,
|
| 795 |
+
y: clockMinimized ? 100 : 0,
|
| 796 |
+
}}
|
| 797 |
+
exit={{ opacity: 0, scale: 0.95 }}
|
| 798 |
+
transition={{ duration: 0.2 }}
|
| 799 |
+
style={{
|
| 800 |
+
pointerEvents: clockMinimized ? 'none' : 'auto',
|
| 801 |
+
display: clockMinimized ? 'none' : 'block'
|
| 802 |
+
}}
|
| 803 |
+
>
|
| 804 |
+
<Clock
|
| 805 |
+
onClose={closeClock}
|
| 806 |
+
onMinimize={() => setClockMinimized(true)}
|
| 807 |
+
onFocus={() => bringWindowToFront('clock')}
|
| 808 |
+
zIndex={windowZIndices.clock || 1000}
|
| 809 |
+
/>
|
| 810 |
+
</motion.div>
|
| 811 |
+
)}
|
| 812 |
+
|
| 813 |
+
|
| 814 |
+
|
| 815 |
+
{geminiChatOpen && (
|
| 816 |
+
<motion.div
|
| 817 |
+
key="gemini"
|
| 818 |
+
initial={{ opacity: 0, scale: 0.95 }}
|
| 819 |
+
animate={{
|
| 820 |
+
opacity: geminiChatMinimized ? 0 : 1,
|
| 821 |
+
scale: geminiChatMinimized ? 0.9 : 1,
|
| 822 |
+
y: geminiChatMinimized ? 100 : 0,
|
| 823 |
+
}}
|
| 824 |
+
exit={{ opacity: 0, scale: 0.95 }}
|
| 825 |
+
transition={{ duration: 0.2 }}
|
| 826 |
+
style={{
|
| 827 |
+
pointerEvents: geminiChatMinimized ? 'none' : 'auto',
|
| 828 |
+
display: geminiChatMinimized ? 'none' : 'block'
|
| 829 |
+
}}
|
| 830 |
+
>
|
| 831 |
+
<GeminiChat
|
| 832 |
+
onClose={closeGeminiChat}
|
| 833 |
+
onMinimize={() => setGeminiChatMinimized(true)}
|
| 834 |
+
onFocus={() => bringWindowToFront('gemini')}
|
| 835 |
+
zIndex={windowZIndices.gemini || 1000}
|
| 836 |
+
/>
|
| 837 |
+
</motion.div>
|
| 838 |
+
)}
|
| 839 |
+
|
| 840 |
+
{sessionManagerOpen && sessionInitialized && (
|
| 841 |
+
<motion.div
|
| 842 |
+
key="session-manager"
|
| 843 |
+
initial={{ opacity: 0, scale: 0.95 }}
|
| 844 |
+
animate={{
|
| 845 |
+
opacity: sessionManagerMinimized ? 0 : 1,
|
| 846 |
+
scale: sessionManagerMinimized ? 0.9 : 1,
|
| 847 |
+
y: sessionManagerMinimized ? 100 : 0,
|
| 848 |
+
}}
|
| 849 |
+
exit={{ opacity: 0, scale: 0.95 }}
|
| 850 |
+
transition={{ duration: 0.2 }}
|
| 851 |
+
style={{
|
| 852 |
+
pointerEvents: sessionManagerMinimized ? 'none' : 'auto',
|
| 853 |
+
display: sessionManagerMinimized ? 'none' : 'block'
|
| 854 |
+
}}
|
| 855 |
+
>
|
| 856 |
<SessionManagerWindow
|
| 857 |
onClose={closeSessionManager}
|
| 858 |
sessionId={userSession}
|
|
|
|
| 862 |
</motion.div>
|
| 863 |
)}
|
| 864 |
|
| 865 |
+
{flutterRunnerOpen && activeFlutterApp && (
|
| 866 |
+
<motion.div
|
| 867 |
+
key="flutter-runner"
|
| 868 |
+
initial={{ opacity: 0, scale: 0.95 }}
|
| 869 |
+
animate={{
|
| 870 |
+
opacity: flutterRunnerMinimized ? 0 : 1,
|
| 871 |
+
scale: flutterRunnerMinimized ? 0.9 : 1,
|
| 872 |
+
y: flutterRunnerMinimized ? 100 : 0,
|
| 873 |
+
}}
|
| 874 |
+
exit={{ opacity: 0, scale: 0.95 }}
|
| 875 |
+
transition={{ duration: 0.2 }}
|
| 876 |
+
style={{
|
| 877 |
+
pointerEvents: flutterRunnerMinimized ? 'none' : 'auto',
|
| 878 |
+
display: flutterRunnerMinimized ? 'none' : 'block'
|
| 879 |
+
}}
|
| 880 |
+
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 881 |
<FlutterRunner
|
| 882 |
initialCode={activeFlutterApp?.dartCode}
|
| 883 |
sessionId={userSession}
|
|
|
|
| 890 |
</motion.div>
|
| 891 |
)}
|
| 892 |
|
| 893 |
+
{researchBrowserOpen && (
|
| 894 |
<motion.div
|
| 895 |
key="research-browser"
|
| 896 |
+
initial={{ opacity: 0, scale: 0.95 }}
|
| 897 |
animate={{
|
| 898 |
+
opacity: researchBrowserMinimized ? 0 : 1,
|
| 899 |
+
scale: researchBrowserMinimized ? 0.9 : 1,
|
| 900 |
+
y: researchBrowserMinimized ? 100 : 0,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 901 |
}}
|
| 902 |
+
exit={{ opacity: 0, scale: 0.95 }}
|
| 903 |
+
transition={{ duration: 0.2 }}
|
| 904 |
style={{
|
| 905 |
+
pointerEvents: researchBrowserMinimized ? 'none' : 'auto',
|
| 906 |
+
display: researchBrowserMinimized ? 'none' : 'block'
|
|
|
|
| 907 |
}}
|
| 908 |
>
|
| 909 |
<ResearchBrowser
|
|
|
|
| 913 |
</motion.div>
|
| 914 |
)}
|
| 915 |
|
| 916 |
+
{flutterCodeEditorOpen && (
|
| 917 |
<motion.div
|
| 918 |
key="flutter-code-editor"
|
| 919 |
+
initial={{ opacity: 0, scale: 0.95 }}
|
| 920 |
animate={{
|
| 921 |
+
opacity: flutterCodeEditorMinimized ? 0 : 1,
|
| 922 |
+
scale: flutterCodeEditorMinimized ? 0.9 : 1,
|
| 923 |
+
y: flutterCodeEditorMinimized ? 100 : 0,
|
| 924 |
}}
|
| 925 |
+
exit={{ opacity: 0, scale: 0.95 }}
|
| 926 |
+
transition={{ duration: 0.2 }}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 927 |
style={{
|
| 928 |
+
pointerEvents: flutterCodeEditorMinimized ? 'none' : 'auto',
|
| 929 |
+
display: flutterCodeEditorMinimized ? 'none' : 'block'
|
|
|
|
| 930 |
}}
|
| 931 |
>
|
| 932 |
<FlutterCodeEditor onClose={closeFlutterCodeEditor} onMinimize={() => setFlutterCodeEditorMinimized(true)} />
|
| 933 |
</motion.div>
|
| 934 |
)}
|
| 935 |
|
| 936 |
+
{latexEditorOpen && (
|
| 937 |
<motion.div
|
| 938 |
key="latex-editor"
|
| 939 |
+
initial={{ opacity: 0, scale: 0.95 }}
|
| 940 |
animate={{
|
| 941 |
+
opacity: latexEditorMinimized ? 0 : 1,
|
| 942 |
+
scale: latexEditorMinimized ? 0.9 : 1,
|
| 943 |
+
y: latexEditorMinimized ? 100 : 0,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 944 |
}}
|
| 945 |
+
exit={{ opacity: 0, scale: 0.95 }}
|
| 946 |
+
transition={{ duration: 0.2 }}
|
| 947 |
style={{
|
| 948 |
+
pointerEvents: latexEditorMinimized ? 'none' : 'auto',
|
| 949 |
+
display: latexEditorMinimized ? 'none' : 'block'
|
|
|
|
| 950 |
}}
|
| 951 |
>
|
| 952 |
<LaTeXEditor onClose={closeLaTeXEditor} sessionId={userSession} onMinimize={() => setLaTeXEditorMinimized(true)} />
|
| 953 |
</motion.div>
|
| 954 |
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 955 |
</AnimatePresence>
|
| 956 |
+
</div>
|
| 957 |
</div>
|
| 958 |
|
| 959 |
{/* Spotlight Search */}
|
app/components/Dock.tsx
CHANGED
|
@@ -31,6 +31,7 @@ interface DockProps {
|
|
| 31 |
onOpenClock: () => void
|
| 32 |
|
| 33 |
onOpenGeminiChat: () => void
|
|
|
|
| 34 |
openApps: { [key: string]: boolean }
|
| 35 |
minimizedApps?: MinimizedApp[]
|
| 36 |
}
|
|
@@ -82,6 +83,7 @@ export function Dock({
|
|
| 82 |
onOpenClock,
|
| 83 |
|
| 84 |
onOpenGeminiChat,
|
|
|
|
| 85 |
openApps,
|
| 86 |
minimizedApps = []
|
| 87 |
}: DockProps) {
|
|
@@ -173,7 +175,7 @@ export function Dock({
|
|
| 173 |
</div>
|
| 174 |
}
|
| 175 |
label="Trash"
|
| 176 |
-
onClick={() =>
|
| 177 |
className=""
|
| 178 |
mouseX={mouseX}
|
| 179 |
/>
|
|
|
|
| 31 |
onOpenClock: () => void
|
| 32 |
|
| 33 |
onOpenGeminiChat: () => void
|
| 34 |
+
onCloseAllApps?: () => void
|
| 35 |
openApps: { [key: string]: boolean }
|
| 36 |
minimizedApps?: MinimizedApp[]
|
| 37 |
}
|
|
|
|
| 83 |
onOpenClock,
|
| 84 |
|
| 85 |
onOpenGeminiChat,
|
| 86 |
+
onCloseAllApps,
|
| 87 |
openApps,
|
| 88 |
minimizedApps = []
|
| 89 |
}: DockProps) {
|
|
|
|
| 175 |
</div>
|
| 176 |
}
|
| 177 |
label="Trash"
|
| 178 |
+
onClick={() => onCloseAllApps?.()}
|
| 179 |
className=""
|
| 180 |
mouseX={mouseX}
|
| 181 |
/>
|
app/components/Window.tsx
CHANGED
|
@@ -10,11 +10,13 @@ interface WindowProps {
|
|
| 10 |
onClose: () => void;
|
| 11 |
onMinimize?: () => void;
|
| 12 |
onMaximize?: () => void;
|
|
|
|
| 13 |
children: ReactNode;
|
| 14 |
width?: number | string;
|
| 15 |
height?: number | string;
|
| 16 |
x?: number;
|
| 17 |
y?: number;
|
|
|
|
| 18 |
resizable?: boolean;
|
| 19 |
className?: string;
|
| 20 |
headerClassName?: string;
|
|
@@ -28,23 +30,24 @@ const Window: React.FC<WindowProps> = ({
|
|
| 28 |
onClose,
|
| 29 |
onMinimize,
|
| 30 |
onMaximize,
|
|
|
|
| 31 |
children,
|
| 32 |
width = 800,
|
| 33 |
height = 600,
|
| 34 |
x = 100,
|
| 35 |
y = 100,
|
|
|
|
| 36 |
resizable = true,
|
| 37 |
className = '',
|
| 38 |
headerClassName = '',
|
| 39 |
darkMode = false,
|
| 40 |
}) => {
|
| 41 |
const [isMaximized, setIsMaximized] = React.useState(false);
|
| 42 |
-
const [previousSize, setPreviousSize] = React.useState({ width, height, x, y });
|
| 43 |
-
const [currentPosition, setCurrentPosition] = React.useState({ x, y });
|
| 44 |
const [currentSize, setCurrentSize] = React.useState({ width, height });
|
| 45 |
const [isMobile, setIsMobile] = React.useState(false);
|
| 46 |
const [isDraggingOrResizing, setIsDraggingOrResizing] = React.useState(false);
|
| 47 |
-
const [zIndex, setZIndex] = React.useState(1000);
|
| 48 |
|
| 49 |
// Detect mobile device
|
| 50 |
useEffect(() => {
|
|
@@ -99,17 +102,19 @@ const Window: React.FC<WindowProps> = ({
|
|
| 99 |
if (onMaximize) onMaximize();
|
| 100 |
};
|
| 101 |
|
| 102 |
-
//
|
| 103 |
const bringToFront = () => {
|
| 104 |
-
//
|
| 105 |
-
|
|
|
|
|
|
|
| 106 |
};
|
| 107 |
|
| 108 |
// Calculate Rnd props based on state
|
| 109 |
const rndProps = isMaximized ? {
|
| 110 |
-
position: { x: 0, y:
|
| 111 |
size: { width: typeof window !== 'undefined' ? window.innerWidth : '100vw',
|
| 112 |
-
height: typeof window !== 'undefined' ? window.innerHeight -
|
| 113 |
disableDragging: true,
|
| 114 |
enableResizing: false
|
| 115 |
} : {
|
|
@@ -124,7 +129,6 @@ const Window: React.FC<WindowProps> = ({
|
|
| 124 |
{...rndProps}
|
| 125 |
minWidth={400}
|
| 126 |
minHeight={300}
|
| 127 |
-
bounds="parent"
|
| 128 |
dragHandleClassName="window-drag-handle"
|
| 129 |
enableResizing={!isMaximized && resizable}
|
| 130 |
disableDragging={isMaximized}
|
|
@@ -133,9 +137,19 @@ const Window: React.FC<WindowProps> = ({
|
|
| 133 |
setIsDraggingOrResizing(true);
|
| 134 |
bringToFront();
|
| 135 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
onDragStop={(e, d) => {
|
| 137 |
setIsDraggingOrResizing(false);
|
| 138 |
-
if (!isMaximized)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
}}
|
| 140 |
onResizeStart={() => {
|
| 141 |
setIsDraggingOrResizing(true);
|
|
@@ -148,14 +162,17 @@ const Window: React.FC<WindowProps> = ({
|
|
| 148 |
width: ref.offsetWidth,
|
| 149 |
height: ref.offsetHeight,
|
| 150 |
});
|
| 151 |
-
|
|
|
|
|
|
|
| 152 |
}
|
| 153 |
}}
|
| 154 |
className="absolute"
|
| 155 |
-
style={{ zIndex }}
|
| 156 |
>
|
| 157 |
<div
|
| 158 |
className={`h-full macos-window flex flex-col ${className} ${windowClass}`}
|
|
|
|
| 159 |
>
|
| 160 |
<div
|
| 161 |
className={`window-drag-handle h-10 flex items-center px-4 space-x-4 border-b cursor-move ${headerClass} ${headerClassName}`}
|
|
|
|
| 10 |
onClose: () => void;
|
| 11 |
onMinimize?: () => void;
|
| 12 |
onMaximize?: () => void;
|
| 13 |
+
onFocus?: () => void;
|
| 14 |
children: ReactNode;
|
| 15 |
width?: number | string;
|
| 16 |
height?: number | string;
|
| 17 |
x?: number;
|
| 18 |
y?: number;
|
| 19 |
+
zIndex?: number;
|
| 20 |
resizable?: boolean;
|
| 21 |
className?: string;
|
| 22 |
headerClassName?: string;
|
|
|
|
| 30 |
onClose,
|
| 31 |
onMinimize,
|
| 32 |
onMaximize,
|
| 33 |
+
onFocus,
|
| 34 |
children,
|
| 35 |
width = 800,
|
| 36 |
height = 600,
|
| 37 |
x = 100,
|
| 38 |
y = 100,
|
| 39 |
+
zIndex = 1000,
|
| 40 |
resizable = true,
|
| 41 |
className = '',
|
| 42 |
headerClassName = '',
|
| 43 |
darkMode = false,
|
| 44 |
}) => {
|
| 45 |
const [isMaximized, setIsMaximized] = React.useState(false);
|
| 46 |
+
const [previousSize, setPreviousSize] = React.useState({ width, height, x, y: Math.max(y, 32) });
|
| 47 |
+
const [currentPosition, setCurrentPosition] = React.useState({ x, y: Math.max(y, 32) });
|
| 48 |
const [currentSize, setCurrentSize] = React.useState({ width, height });
|
| 49 |
const [isMobile, setIsMobile] = React.useState(false);
|
| 50 |
const [isDraggingOrResizing, setIsDraggingOrResizing] = React.useState(false);
|
|
|
|
| 51 |
|
| 52 |
// Detect mobile device
|
| 53 |
useEffect(() => {
|
|
|
|
| 102 |
if (onMaximize) onMaximize();
|
| 103 |
};
|
| 104 |
|
| 105 |
+
// Bring window to front
|
| 106 |
const bringToFront = () => {
|
| 107 |
+
// Call onFocus to bring window to front
|
| 108 |
+
if (onFocus) {
|
| 109 |
+
onFocus();
|
| 110 |
+
}
|
| 111 |
};
|
| 112 |
|
| 113 |
// Calculate Rnd props based on state
|
| 114 |
const rndProps = isMaximized ? {
|
| 115 |
+
position: { x: 0, y: 32 }, // 32px for TopBar offset (h-8 = 32px)
|
| 116 |
size: { width: typeof window !== 'undefined' ? window.innerWidth : '100vw',
|
| 117 |
+
height: typeof window !== 'undefined' ? window.innerHeight - 32 : 'calc(100vh - 32px)' },
|
| 118 |
disableDragging: true,
|
| 119 |
enableResizing: false
|
| 120 |
} : {
|
|
|
|
| 129 |
{...rndProps}
|
| 130 |
minWidth={400}
|
| 131 |
minHeight={300}
|
|
|
|
| 132 |
dragHandleClassName="window-drag-handle"
|
| 133 |
enableResizing={!isMaximized && resizable}
|
| 134 |
disableDragging={isMaximized}
|
|
|
|
| 137 |
setIsDraggingOrResizing(true);
|
| 138 |
bringToFront();
|
| 139 |
}}
|
| 140 |
+
onDrag={(e, d) => {
|
| 141 |
+
// Prevent dragging above TopBar during drag
|
| 142 |
+
if (d.y < 32) {
|
| 143 |
+
return false;
|
| 144 |
+
}
|
| 145 |
+
}}
|
| 146 |
onDragStop={(e, d) => {
|
| 147 |
setIsDraggingOrResizing(false);
|
| 148 |
+
if (!isMaximized) {
|
| 149 |
+
// Ensure window doesn't go above TopBar
|
| 150 |
+
const newY = Math.max(d.y, 32);
|
| 151 |
+
setCurrentPosition({ x: d.x, y: newY });
|
| 152 |
+
}
|
| 153 |
}}
|
| 154 |
onResizeStart={() => {
|
| 155 |
setIsDraggingOrResizing(true);
|
|
|
|
| 162 |
width: ref.offsetWidth,
|
| 163 |
height: ref.offsetHeight,
|
| 164 |
});
|
| 165 |
+
// Ensure window doesn't go above TopBar after resize
|
| 166 |
+
const newY = Math.max(position.y, 32);
|
| 167 |
+
setCurrentPosition({ ...position, y: newY });
|
| 168 |
}
|
| 169 |
}}
|
| 170 |
className="absolute"
|
| 171 |
+
style={{ zIndex: zIndex || 1000 }}
|
| 172 |
>
|
| 173 |
<div
|
| 174 |
className={`h-full macos-window flex flex-col ${className} ${windowClass}`}
|
| 175 |
+
onMouseDown={bringToFront}
|
| 176 |
>
|
| 177 |
<div
|
| 178 |
className={`window-drag-handle h-10 flex items-center px-4 space-x-4 border-b cursor-move ${headerClass} ${headerClassName}`}
|