diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index adffeec..10b0a8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' - name: Install dependencies run: npm install @@ -40,7 +40,7 @@ jobs: # - name: Set up Node.js # uses: actions/setup-node@v4 # with: -# node-version: '20' +# node-version: '22' # - name: Install dependencies # run: npm install # - name: Run tests diff --git a/App.tsx b/App.tsx index 0cb607b..a2fbe3b 100644 --- a/App.tsx +++ b/App.tsx @@ -1,50 +1,67 @@ - -import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; -import { IndicatorStyle, WindowState, Workspace, UserPreferences, TabData, ColorTheme, FocusTimerState, TodoItem, Priority, SoundscapeType } from './types'; -import StatusBar from './StatusBar'; -import Desktop from './Desktop'; -import Onboarding from './Onboarding'; -import PreferencesModal from './PreferencesModal'; -import WorkspaceManager from './WorkspaceManager'; -import SmartAssistant from './SmartAssistant'; -import GhostSearch from './GhostSearch'; -import EchoAssistant from './EchoAssistant'; +import React, { + useState, + useEffect, + useCallback, + useMemo, + useRef, +} from "react"; +import { + IndicatorStyle, + WindowState, + Workspace, + UserPreferences, + TabData, + ColorTheme, + FocusTimerState, + TodoItem, + Priority, + SoundscapeType, +} from "./types"; +import StatusBar from "./StatusBar"; +import Desktop from "./Desktop"; +import Onboarding from "./Onboarding"; +import PreferencesModal from "./PreferencesModal"; +import WorkspaceManager from "./WorkspaceManager"; +import SmartAssistant from "./SmartAssistant"; +import GhostSearch from "./GhostSearch"; +import EchoAssistant from "./EchoAssistant"; import { GoogleGenAI, Type } from "@google/genai"; -import { soundscapeEngine } from './soundscapeEngine'; +import { soundscapeEngine } from "./soundscapeEngine"; const THEME_ACCENTS: Record = { - nebula: '#3b82f6', - sunrise: '#f97316', - ocean: '#06b6d4', - emerald: '#10b981', + nebula: "#3b82f6", + sunrise: "#f97316", + ocean: "#06b6d4", + emerald: "#10b981", }; const DEFAULT_PREFERENCES: UserPreferences = { launchAtLogin: false, showMenuBarIcon: true, indicatorStyle: IndicatorStyle.COLORED_GLOW, - accentColor: '#3b82f6', + accentColor: "#3b82f6", + theme: "dark", minOpacity: 0.4, zenMode: false, ghostingEnabled: true, - colorTheme: 'nebula', - soundscape: 'none', + colorTheme: "nebula", + soundscape: "none", snapZonesEnabled: true, shortcuts: { - pin: 'p', - transparency: 't', - focus: 'f', - workspace: 'w', - zen: 'z', - search: 'k', + pin: "p", + transparency: "t", + focus: "f", + workspace: "w", + zen: "z", + search: "k", }, }; const INITIAL_WINDOWS: WindowState[] = [ { - id: 'w1', - title: 'Research Project', - type: 'browser', + id: "w1", + title: "Research Project", + type: "browser", x: 350, y: 100, width: 600, @@ -52,17 +69,38 @@ const INITIAL_WINDOWS: WindowState[] = [ isPinned: true, opacity: 1, focusCount: 8, - content: 'Deep dive into the architecture of modern LLMs.', + content: "Deep dive into the architecture of modern LLMs.", tabs: [ - { id: 't1', title: 'Gemini Technical Paper', url: 'deepmind.google/gemini', isActive: true, category: 'AI Research', lastAccessed: Date.now() }, - { id: 't2', title: 'React Performance Tips', url: 'react.dev/learn', isActive: false, category: 'Dev Docs', lastAccessed: Date.now() - 10000 }, - { id: 't3', title: 'Window Management UX', url: 'nngroup.com', isActive: false, category: 'Design', lastAccessed: Date.now() - 20000 } - ] + { + id: "t1", + title: "Gemini Technical Paper", + url: "deepmind.google/gemini", + isActive: true, + category: "AI Research", + lastAccessed: Date.now(), + }, + { + id: "t2", + title: "React Performance Tips", + url: "react.dev/learn", + isActive: false, + category: "Dev Docs", + lastAccessed: Date.now() - 10000, + }, + { + id: "t3", + title: "Window Management UX", + url: "nngroup.com", + isActive: false, + category: "Design", + lastAccessed: Date.now() - 20000, + }, + ], }, { - id: 'w2', - title: 'Focus Space', - type: 'notes', + id: "w2", + title: "Focus Space", + type: "notes", x: 800, y: 400, width: 300, @@ -70,12 +108,12 @@ const INITIAL_WINDOWS: WindowState[] = [ isPinned: false, opacity: 1, focusCount: 3, - content: 'Goal: Finish the multi-tab architecture by end of day.', + content: "Goal: Finish the multi-tab architecture by end of day.", }, { - id: 'w3', - title: 'Creative Canvas', - type: 'canvas', + id: "w3", + title: "Creative Canvas", + type: "canvas", x: 100, y: 180, width: 320, @@ -84,18 +122,20 @@ const INITIAL_WINDOWS: WindowState[] = [ opacity: 1, isClosed: true, focusCount: 1, - content: 'Generative mood board.', + content: "Generative mood board.", canvasData: { - prompt: 'A futuristic digital workspace with floating holographic screens in Nebula colors', - imageUrl: '', - isGenerating: false - } - } + prompt: + "A futuristic digital workspace with floating holographic screens in Nebula colors", + imageUrl: "", + isGenerating: false, + }, + }, ]; const App: React.FC = () => { const [isOnboarding, setIsOnboarding] = useState(true); - const [preferences, setPreferences] = useState(DEFAULT_PREFERENCES); + const [preferences, setPreferences] = + useState(DEFAULT_PREFERENCES); const [windows, setWindows] = useState(INITIAL_WINDOWS); const [todos, setTodos] = useState([]); const [archivedTabs, setArchivedTabs] = useState([]); @@ -108,18 +148,18 @@ const App: React.FC = () => { const [workspaces, setWorkspaces] = useState([]); const [isMobile, setIsMobile] = useState(false); const [globalWallpaper, setGlobalWallpaper] = useState(null); - + const [focusTimer, setFocusTimer] = useState({ isActive: false, timeLeft: 25 * 60, - mode: 'focus' + mode: "focus", }); const [soundscapeVolume, setSoundscapeVolume] = useState(0.5); // Soundscape audio controller useEffect(() => { - if (preferences.soundscape && preferences.soundscape !== 'none') { + if (preferences.soundscape && preferences.soundscape !== "none") { soundscapeEngine.start(preferences.soundscape); } else { soundscapeEngine.stop(); @@ -139,10 +179,10 @@ const App: React.FC = () => { let interval: any; if (focusTimer.isActive && focusTimer.timeLeft > 0) { interval = setInterval(() => { - setFocusTimer(prev => ({ ...prev, timeLeft: prev.timeLeft - 1 })); + setFocusTimer((prev) => ({ ...prev, timeLeft: prev.timeLeft - 1 })); }, 1000); } else if (focusTimer.timeLeft === 0) { - setFocusTimer(prev => ({ ...prev, isActive: false })); + setFocusTimer((prev) => ({ ...prev, isActive: false })); } return () => clearInterval(interval); }, [focusTimer.isActive, focusTimer.timeLeft]); @@ -151,245 +191,334 @@ const App: React.FC = () => { useEffect(() => { const checkMobile = () => setIsMobile(window.innerWidth < 768); checkMobile(); - window.addEventListener('resize', checkMobile); - return () => window.removeEventListener('resize', checkMobile); + window.addEventListener("resize", checkMobile); + return () => window.removeEventListener("resize", checkMobile); }, []); - const toggleFocusTimer = () => setFocusTimer(prev => ({ ...prev, isActive: !prev.isActive })); - - const updateWindowPosition = useCallback((id: string, x: number, y: number) => { - if (isMobile) return; - setWindows(prev => prev.map(w => { - if (w.id !== id) return w; - if (w.preSnapDimensions) { - const pre = w.preSnapDimensions; - return { - ...w, - x: x + (w.width - pre.width) / 2, - y, - width: pre.width, - height: pre.height, - preSnapDimensions: undefined - }; - } - return { ...w, x, y }; - })); - }, [isMobile]); - - const handleSnapWindow = useCallback((id: string, snapType: 'left' | 'right' | 'top' | 'bottom' | 'none') => { - if (isMobile) return; - setWindows(prev => prev.map(w => { - if (w.id !== id) return w; - - const width = window.innerWidth; - const height = window.innerHeight; - const statusBarHeight = 32; // pt-8 is 32px - const desktopHeight = height - statusBarHeight; - - if (snapType === 'none') return w; + const toggleFocusTimer = () => + setFocusTimer((prev) => ({ ...prev, isActive: !prev.isActive })); + + const updateWindowPosition = useCallback( + (id: string, x: number, y: number) => { + if (isMobile) return; + setWindows((prev) => + prev.map((w) => { + if (w.id !== id) return w; + if (w.preSnapDimensions) { + const pre = w.preSnapDimensions; + return { + ...w, + x: x + (w.width - pre.width) / 2, + y, + width: pre.width, + height: pre.height, + preSnapDimensions: undefined, + }; + } + return { ...w, x, y }; + }), + ); + }, + [isMobile], + ); - const preSnapDimensions = w.preSnapDimensions || { x: w.x, y: w.y, width: w.width, height: w.height }; + const handleSnapWindow = useCallback( + (id: string, snapType: "left" | "right" | "top" | "bottom" | "none") => { + if (isMobile) return; + setWindows((prev) => + prev.map((w) => { + if (w.id !== id) return w; + + const width = window.innerWidth; + const height = window.innerHeight; + const statusBarHeight = 32; // pt-8 is 32px + const desktopHeight = height - statusBarHeight; + + if (snapType === "none") return w; + + const preSnapDimensions = w.preSnapDimensions || { + x: w.x, + y: w.y, + width: w.width, + height: w.height, + }; - if (snapType === 'left') { - return { - ...w, - x: 0, - y: statusBarHeight, - width: width / 2, - height: desktopHeight, - isPinned: true, - preSnapDimensions - }; - } else if (snapType === 'right') { - return { - ...w, - x: width / 2, - y: statusBarHeight, - width: width / 2, - height: desktopHeight, - isPinned: true, - preSnapDimensions - }; - } else if (snapType === 'top') { - return { - ...w, - x: 0, - y: statusBarHeight, - width: width, - height: desktopHeight, - isPinned: true, - preSnapDimensions - }; - } else if (snapType === 'bottom') { - return { - ...w, - x: 0, - y: statusBarHeight + desktopHeight / 2, - width: width, - height: desktopHeight / 2, - isPinned: true, - preSnapDimensions - }; - } - return w; - })); - }, [isMobile]); + if (snapType === "left") { + return { + ...w, + x: 0, + y: statusBarHeight, + width: width / 2, + height: desktopHeight, + isPinned: true, + preSnapDimensions, + }; + } else if (snapType === "right") { + return { + ...w, + x: width / 2, + y: statusBarHeight, + width: width / 2, + height: desktopHeight, + isPinned: true, + preSnapDimensions, + }; + } else if (snapType === "top") { + return { + ...w, + x: 0, + y: statusBarHeight, + width: width, + height: desktopHeight, + isPinned: true, + preSnapDimensions, + }; + } else if (snapType === "bottom") { + return { + ...w, + x: 0, + y: statusBarHeight + desktopHeight / 2, + width: width, + height: desktopHeight / 2, + isPinned: true, + preSnapDimensions, + }; + } + return w; + }), + ); + }, + [isMobile], + ); const togglePin = useCallback((id: string) => { - setWindows(prev => prev.map(w => w.id === id ? { ...w, isPinned: !w.isPinned } : w)); + setWindows((prev) => + prev.map((w) => (w.id === id ? { ...w, isPinned: !w.isPinned } : w)), + ); }, []); - const handleGenerateCanvas = useCallback(async (id: string, prompt: string) => { - setWindows(prev => prev.map(w => w.id === id ? { ...w, canvasData: { ...w.canvasData, isGenerating: true, prompt } } : w)); - - try { - const ai = new GoogleGenAI({ apiKey: process.env.API_KEY }); - const response = await ai.models.generateContent({ - model: 'gemini-2.5-flash-image', - contents: { parts: [{ text: prompt }] }, - config: { imageConfig: { aspectRatio: "1:1" } } - }); + const handleGenerateCanvas = useCallback( + async (id: string, prompt: string) => { + setWindows((prev) => + prev.map((w) => + w.id === id + ? { + ...w, + canvasData: { ...w.canvasData, isGenerating: true, prompt }, + } + : w, + ), + ); + + try { + const ai = new GoogleGenAI({ apiKey: process.env.API_KEY }); + const response = await ai.models.generateContent({ + model: "gemini-2.5-flash-image", + contents: { parts: [{ text: prompt }] }, + config: { imageConfig: { aspectRatio: "1:1" } }, + }); - let imageUrl = ''; - for (const part of response.candidates?.[0]?.content?.parts || []) { - if (part.inlineData) { - imageUrl = `data:${part.inlineData.mimeType};base64,${part.inlineData.data}`; - break; + let imageUrl = ""; + for (const part of response.candidates?.[0]?.content?.parts || []) { + if (part.inlineData) { + imageUrl = `data:${part.inlineData.mimeType};base64,${part.inlineData.data}`; + break; + } } - } - setWindows(prev => prev.map(w => w.id === id ? { - ...w, - canvasData: { ...w.canvasData, isGenerating: false, imageUrl, prompt } - } : w)); - } catch (e) { - console.error("Canvas Generation Failed:", e); - setWindows(prev => prev.map(w => w.id === id ? { ...w, canvasData: { ...w.canvasData, isGenerating: false } } : w)); - } - }, []); + setWindows((prev) => + prev.map((w) => + w.id === id + ? { + ...w, + canvasData: { + ...w.canvasData, + isGenerating: false, + imageUrl, + prompt, + }, + } + : w, + ), + ); + } catch (e) { + console.error("Canvas Generation Failed:", e); + setWindows((prev) => + prev.map((w) => + w.id === id + ? { ...w, canvasData: { ...w.canvasData, isGenerating: false } } + : w, + ), + ); + } + }, + [], + ); const handleApplyWallpaper = useCallback((imageUrl: string) => { setGlobalWallpaper(imageUrl); }, []); - const handleSmartStack = useCallback(async (windowId: string) => { - const targetWindow = windows.find(w => w.id === windowId); - if (!targetWindow || !targetWindow.tabs || targetWindow.tabs.length < 2) return; - - try { - const ai = new GoogleGenAI({ apiKey: process.env.API_KEY }); - const tabData = targetWindow.tabs.map(t => ({ id: t.id, title: t.title })); - - const response = await ai.models.generateContent({ - model: 'gemini-3-flash-preview', - contents: `Categorize these browser tabs into logical groups. Return a JSON array of objects with "id" and "category" (max 2 words). Tabs: ${JSON.stringify(tabData)}`, - config: { - responseMimeType: 'application/json', - responseSchema: { - type: Type.ARRAY, - items: { - type: Type.OBJECT, - properties: { - id: { type: Type.STRING }, - category: { type: Type.STRING } + const handleSmartStack = useCallback( + async (windowId: string) => { + const targetWindow = windows.find((w) => w.id === windowId); + if (!targetWindow || !targetWindow.tabs || targetWindow.tabs.length < 2) + return; + + try { + const ai = new GoogleGenAI({ apiKey: process.env.API_KEY }); + const tabData = targetWindow.tabs.map((t) => ({ + id: t.id, + title: t.title, + })); + + const response = await ai.models.generateContent({ + model: "gemini-3-flash-preview", + contents: `Categorize these browser tabs into logical groups. Return a JSON array of objects with "id" and "category" (max 2 words). Tabs: ${JSON.stringify(tabData)}`, + config: { + responseMimeType: "application/json", + responseSchema: { + type: Type.ARRAY, + items: { + type: Type.OBJECT, + properties: { + id: { type: Type.STRING }, + category: { type: Type.STRING }, + }, + required: ["id", "category"], }, - required: ['id', 'category'] - } - } - } - }); + }, + }, + }); - const categories = JSON.parse(response.text || '[]'); - - setWindows(prev => prev.map(w => { + const categories = JSON.parse(response.text || "[]"); + + setWindows((prev) => + prev.map((w) => { + if (w.id !== windowId || !w.tabs) return w; + return { + ...w, + tabs: w.tabs.map((t) => { + const cat = categories.find((c: any) => c.id === t.id); + return cat ? { ...t, category: cat.category } : t; + }), + }; + }), + ); + } catch (e) { + console.error("Smart Stack Failed:", e); + } + }, + [windows], + ); + + const handleSwitchTab = useCallback((windowId: string, tabId: string) => { + setWindows((prev) => + prev.map((w) => { if (w.id !== windowId || !w.tabs) return w; return { ...w, - tabs: w.tabs.map(t => { - const cat = categories.find((c: any) => c.id === t.id); - return cat ? { ...t, category: cat.category } : t; - }) + tabs: w.tabs.map((t) => ({ + ...t, + isActive: t.id === tabId, + lastAccessed: t.id === tabId ? Date.now() : t.lastAccessed, + })), }; - })); - } catch (e) { - console.error("Smart Stack Failed:", e); - } - }, [windows]); - - const handleSwitchTab = useCallback((windowId: string, tabId: string) => { - setWindows(prev => prev.map(w => { - if (w.id !== windowId || !w.tabs) return w; - return { - ...w, - tabs: w.tabs.map(t => ({ ...t, isActive: t.id === tabId, lastAccessed: t.id === tabId ? Date.now() : t.lastAccessed })) - }; - })); + }), + ); }, []); - const handleUpdateTabCategory = useCallback((windowId: string, tabId: string, category: string) => { - setWindows(prev => prev.map(w => { - if (w.id !== windowId || !w.tabs) return w; - return { - ...w, - tabs: w.tabs.map(t => t.id === tabId ? { ...t, category } : t) - }; - })); - }, []); + const handleUpdateTabCategory = useCallback( + (windowId: string, tabId: string, category: string) => { + setWindows((prev) => + prev.map((w) => { + if (w.id !== windowId || !w.tabs) return w; + return { + ...w, + tabs: w.tabs.map((t) => (t.id === tabId ? { ...t, category } : t)), + }; + }), + ); + }, + [], + ); const handleAddTab = useCallback((windowId: string) => { - setWindows(prev => prev.map(w => { - if (w.id !== windowId) return w; - const newTab: TabData = { - id: `tab-${Date.now()}`, - title: 'New Surface', - url: 'pinpoint.internal/new', - isActive: true, - lastAccessed: Date.now() - }; - return { - ...w, - tabs: [...(w.tabs || []).map(t => ({ ...t, isActive: false })), newTab] - }; - })); + setWindows((prev) => + prev.map((w) => { + if (w.id !== windowId) return w; + const newTab: TabData = { + id: `tab-${Date.now()}`, + title: "New Surface", + url: "pinpoint.internal/new", + isActive: true, + lastAccessed: Date.now(), + }; + return { + ...w, + tabs: [ + ...(w.tabs || []).map((t) => ({ ...t, isActive: false })), + newTab, + ], + }; + }), + ); }, []); const handleCloseTab = useCallback((windowId: string, tabId: string) => { - setWindows(prev => prev.map(w => { - if (w.id !== windowId || !w.tabs) return w; - const tabToArchive = w.tabs.find(t => t.id === tabId); - if (tabToArchive) { - setArchivedTabs(prevArchive => [tabToArchive, ...prevArchive.slice(0, 19)]); - } - const filteredTabs = w.tabs.filter(t => t.id !== tabId); - if (filteredTabs.length > 0 && !filteredTabs.some(t => t.isActive)) { - filteredTabs[0].isActive = true; - } - return { ...w, tabs: filteredTabs }; - })); + setWindows((prev) => + prev.map((w) => { + if (w.id !== windowId || !w.tabs) return w; + const tabToArchive = w.tabs.find((t) => t.id === tabId); + if (tabToArchive) { + setArchivedTabs((prevArchive) => [ + tabToArchive, + ...prevArchive.slice(0, 19), + ]); + } + const filteredTabs = w.tabs.filter((t) => t.id !== tabId); + if (filteredTabs.length > 0 && !filteredTabs.some((t) => t.isActive)) { + filteredTabs[0].isActive = true; + } + return { ...w, tabs: filteredTabs }; + }), + ); }, []); const handleCloseWindow = useCallback((id: string) => { - setWindows(prev => prev.map(w => w.id === id ? { ...w, isClosed: true } : w)); + setWindows((prev) => + prev.map((w) => (w.id === id ? { ...w, isClosed: true } : w)), + ); }, []); const handleRestoreWindow = useCallback((id: string) => { - setWindows(prev => prev.map(w => w.id === id ? { ...w, isClosed: false } : w)); + setWindows((prev) => + prev.map((w) => (w.id === id ? { ...w, isClosed: false } : w)), + ); }, []); const handleToggleCollapseWindow = useCallback((id: string) => { - setWindows(prev => prev.map(w => w.id === id ? { ...w, isCollapsed: !w.isCollapsed } : w)); + setWindows((prev) => + prev.map((w) => + w.id === id ? { ...w, isCollapsed: !w.isCollapsed } : w, + ), + ); }, []); const handleSetActiveWindowId = useCallback((id: string | null) => { setActiveWindowId(id); if (id) { - setWindows(prev => prev.map(w => w.id === id ? { ...w, focusCount: (w.focusCount || 0) + 1 } : w)); + setWindows((prev) => + prev.map((w) => + w.id === id ? { ...w, focusCount: (w.focusCount || 0) + 1 } : w, + ), + ); } }, []); const handleDesktopTidy = useCallback(() => { - setWindows(prev => { - const visibleWindows = prev.filter(w => !w.isClosed); + setWindows((prev) => { + const visibleWindows = prev.filter((w) => !w.isClosed); if (visibleWindows.length === 0) return prev; // Sort by focusCount (usage frequency) descending @@ -402,7 +531,7 @@ const App: React.FC = () => { const screenWidth = window.innerWidth || 1200; const screenHeight = window.innerHeight || 800; - + const centerWidth = Math.max(500, Math.min(680, screenWidth * 0.55)); const centerHeight = Math.max(380, Math.min(500, screenHeight * 0.6)); const centerX = (screenWidth - centerWidth) / 2; @@ -411,7 +540,7 @@ const App: React.FC = () => { const mostActive = sorted[0]; const others = sorted.slice(1); - return prev.map(w => { + return prev.map((w) => { if (w.isClosed) return w; if (w.id === mostActive.id) { @@ -426,17 +555,25 @@ const App: React.FC = () => { }; } - const index = others.findIndex(o => o.id === w.id); + const index = others.findIndex((o) => o.id === w.id); if (index === -1) return w; const isLeft = index % 2 === 0; const sideIndex = Math.floor(index / 2); const totalOnSide = Math.ceil(others.length / 2); - const sideWidth = Math.max(280, Math.min(320, (screenWidth - centerWidth) / 2 - 30)); - const sideX = isLeft + const sideWidth = Math.max( + 280, + Math.min(320, (screenWidth - centerWidth) / 2 - 30), + ); + const sideX = isLeft ? Math.max(20, (centerX - sideWidth) / 2) - : Math.min(screenWidth - sideWidth - 20, centerX + centerWidth + (screenWidth - (centerX + centerWidth) - sideWidth) / 2); + : Math.min( + screenWidth - sideWidth - 20, + centerX + + centerWidth + + (screenWidth - (centerX + centerWidth) - sideWidth) / 2, + ); const availableHeight = screenHeight - 120; const sideHeight = Math.min(260, availableHeight / totalOnSide - 20); @@ -455,8 +592,8 @@ const App: React.FC = () => { }); }); - setWindows(prev => { - const visibleWindows = prev.filter(w => !w.isClosed); + setWindows((prev) => { + const visibleWindows = prev.filter((w) => !w.isClosed); if (visibleWindows.length > 0) { const sorted = [...visibleWindows].sort((a, b) => { const countA = a.focusCount || 0; @@ -470,42 +607,84 @@ const App: React.FC = () => { }); }, []); - const handleRestoreTab = useCallback((tab: TabData) => { - // Restore to currently active window or first browser window - setWindows(prev => { - let targetId = activeWindowId || prev.find(w => w.type === 'browser')?.id; - if (!targetId) return prev; + const handleRestoreTab = useCallback( + (tab: TabData) => { + // Restore to currently active window or first browser window + setWindows((prev) => { + let targetId = + activeWindowId || prev.find((w) => w.type === "browser")?.id; + if (!targetId) return prev; - return prev.map(w => { - if (w.id !== targetId) return w; - return { - ...w, - tabs: [...(w.tabs || []).map(t => ({ ...t, isActive: false })), { ...tab, isActive: true, lastAccessed: Date.now() }] - }; + return prev.map((w) => { + if (w.id !== targetId) return w; + return { + ...w, + tabs: [ + ...(w.tabs || []).map((t) => ({ ...t, isActive: false })), + { ...tab, isActive: true, lastAccessed: Date.now() }, + ], + }; + }); }); - }); - setArchivedTabs(prev => prev.filter(t => t.id !== tab.id)); - }, [activeWindowId]); + setArchivedTabs((prev) => prev.filter((t) => t.id !== tab.id)); + }, + [activeWindowId], + ); - const addIntention = useCallback((text: string, priority: Priority = 'medium') => { - setTodos(prev => [{ id: `t-${Date.now()}`, text, priority, completed: false, createdAt: Date.now() }, ...prev]); - }, []); + const addIntention = useCallback( + (text: string, priority: Priority = "medium") => { + setTodos((prev) => [ + { + id: `t-${Date.now()}`, + text, + priority, + completed: false, + createdAt: Date.now(), + }, + ...prev, + ]); + }, + [], + ); if (isOnboarding) { - return setIsOnboarding(false)} preferences={preferences} setPreferences={setPreferences} />; + return ( + setIsOnboarding(false)} + preferences={preferences} + setPreferences={setPreferences} + /> + ); } return ( -
- w.isPinned).length} +
+ w.isPinned).length} focusMode={preferences.zenMode} currentTheme={preferences.colorTheme} + theme={preferences.theme} + onToggleTheme={() => + setPreferences((p) => ({ + ...p, + theme: p.theme === "dark" ? "light" : "dark", + })) + } + accentColor={preferences.accentColor} + onAccentChange={(color) => + setPreferences((p) => ({ ...p, accentColor: color })) + } focusTimer={focusTimer} archivedTabs={archivedTabs} onRestoreTab={handleRestoreTab} onToggleFocusTimer={toggleFocusTimer} - onThemeChange={(theme) => setPreferences(p => ({ ...p, colorTheme: theme }))} + onThemeChange={(theme) => + setPreferences((p) => ({ ...p, colorTheme: theme })) + } onOpenPrefs={() => setIsPreferencesOpen(true)} onOpenWorkspaces={() => setIsWorkspaceManagerOpen(true)} onOpenAssistant={() => setIsAssistantOpen(true)} @@ -513,14 +692,16 @@ const App: React.FC = () => { onOpenEcho={() => setIsEchoOpen(true)} isMobile={isMobile} soundscape={preferences.soundscape} - onSoundscapeChange={(sound) => setPreferences(p => ({ ...p, soundscape: sound }))} + onSoundscapeChange={(sound) => + setPreferences((p) => ({ ...p, soundscape: sound })) + } soundscapeVolume={soundscapeVolume} onSoundscapeVolumeChange={(vol) => setSoundscapeVolume(vol)} - closedWindows={windows.filter(w => w.isClosed)} + closedWindows={windows.filter((w) => w.isClosed)} onRestoreWindow={handleRestoreWindow} /> - - { setActiveWindowId={handleSetActiveWindowId} updateWindowPosition={updateWindowPosition} togglePin={togglePin} - updateWindowOpacity={(id, opacity) => setWindows(prev => prev.map(w => w.id === id ? { ...w, opacity } : w))} + updateWindowOpacity={(id, opacity) => + setWindows((prev) => + prev.map((w) => (w.id === id ? { ...w, opacity } : w)), + ) + } onDetachTab={handleCloseTab} onSwitchTab={handleSwitchTab} onUpdateTabCategory={handleUpdateTabCategory} @@ -540,9 +725,17 @@ const App: React.FC = () => { onGenerateCanvas={handleGenerateCanvas} onApplyWallpaper={handleApplyWallpaper} onSmartStack={handleSmartStack} - onToggleTodo={(id) => setTodos(prev => prev.map(t => t.id === id ? { ...t, completed: !t.completed } : t))} + onToggleTodo={(id) => + setTodos((prev) => + prev.map((t) => + t.id === id ? { ...t, completed: !t.completed } : t, + ), + ) + } onAddTodo={addIntention} - onDeleteTodo={(id) => setTodos(prev => prev.filter(t => t.id !== id))} + onDeleteTodo={(id) => + setTodos((prev) => prev.filter((t) => t.id !== id)) + } onGroupWindows={() => {}} onRemoveFromFolder={() => {}} isMobile={isMobile} @@ -553,22 +746,32 @@ const App: React.FC = () => { /> {isEchoOpen && ( - setIsEchoOpen(false)} commands={{ - setTimer: (mins) => setFocusTimer({ mode: 'focus', timeLeft: mins * 60, isActive: true }), - setTheme: (theme) => setPreferences(p => ({ ...p, colorTheme: theme as ColorTheme })), + setTimer: (mins) => + setFocusTimer({ + mode: "focus", + timeLeft: mins * 60, + isActive: true, + }), + setTheme: (theme) => + setPreferences((p) => ({ + ...p, + colorTheme: theme as ColorTheme, + })), pinWindow: (id) => togglePin(id), addIntention: (text) => addIntention(text), - toggleZen: () => setPreferences(p => ({ ...p, zenMode: !p.zenMode })), - desktopTidy: handleDesktopTidy + toggleZen: () => + setPreferences((p) => ({ ...p, zenMode: !p.zenMode })), + desktopTidy: handleDesktopTidy, }} /> )} {isAssistantOpen && ( - {}} onCategorize={() => {}} @@ -578,10 +781,33 @@ const App: React.FC = () => { isMobile={isMobile} /> )} - - {isSearchOpen && { handleSetActiveWindowId(id); setIsSearchOpen(false); }} onClose={() => setIsSearchOpen(false)} />} - {isPreferencesOpen && setIsPreferencesOpen(false)} isMobile={isMobile} />} - {isWorkspaceManagerOpen && {}} onRestore={() => {}} onClose={() => setIsWorkspaceManagerOpen(false)} />} + + {isSearchOpen && ( + { + handleSetActiveWindowId(id); + setIsSearchOpen(false); + }} + onClose={() => setIsSearchOpen(false)} + /> + )} + {isPreferencesOpen && ( + setIsPreferencesOpen(false)} + isMobile={isMobile} + /> + )} + {isWorkspaceManagerOpen && ( + {}} + onRestore={() => {}} + onClose={() => setIsWorkspaceManagerOpen(false)} + /> + )}
); }; diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 3b4944c..3f41a98 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -127,6 +127,11 @@ # - **Date:** 2026-09-07 # - **One thing I learned:** Contributing to this repository is one of the fastest ways for me to grow as a developer,it has shifted my mindset from working in isolation to building software as a team. +# ## Adegbola Adeniyi +# - **GitHub:** @niyexdroid +# - **Contribution:** Cloud & DevOps Foundation – Phase 2 Project [Feat: Added a dark and light mode with color picker for the buttons,widget, etc] +# - **Date:** 2026-09-09 +# - **One thing I learned:** Always remember to checkout your branch or switch your branch so it can be recognized by the upstream. # ## Olakunle Bayode # - **GitHub:** @bayodeolamikunle # - **Contribution:** Cloud & DevOps Foundation – Phase 2 Project diff --git a/ColorStylePicker.tsx b/ColorStylePicker.tsx new file mode 100644 index 0000000..f20cd09 --- /dev/null +++ b/ColorStylePicker.tsx @@ -0,0 +1,87 @@ +import React, { useState } from "react"; +import { ACCENT_PRESETS } from "./types"; +import { Palette } from "lucide-react"; + +interface Props { + accentColor: string; + onChange: (color: string) => void; + isMobile?: boolean; +} + +const ColorStylePicker: React.FC = ({ + accentColor, + onChange, + isMobile, +}) => { + const [isOpen, setIsOpen] = useState(false); + + return ( +
+ + + {isOpen && ( +
+
+

+ Accent Color +

+ +
+ +
+ {ACCENT_PRESETS.map((preset) => ( + + ))} +
+ + onChange(e.target.value)} + className="mt-3 w-full h-8 rounded-lg bg-transparent border border-white/10 cursor-pointer" + title="Custom color" + /> +
+ )} +
+ ); +}; + +export default ColorStylePicker; diff --git a/PreferencesModal.tsx b/PreferencesModal.tsx index 3992572..908777e 100644 --- a/PreferencesModal.tsx +++ b/PreferencesModal.tsx @@ -1,7 +1,13 @@ - -import React, { useState } from 'react'; -import { UserPreferences, IndicatorStyle, SoundscapeType, ColorTheme } from './types'; -import { Pin } from 'lucide-react'; +import React, { useState } from "react"; +import { + UserPreferences, + IndicatorStyle, + SoundscapeType, + ColorTheme, + ACCENT_PRESETS, + AppTheme, +} from "./types"; +import { Pin } from "lucide-react"; // Added isMobile prop to interface to fix TypeScript error in App.tsx interface Props { @@ -11,50 +17,73 @@ interface Props { isMobile?: boolean; } -const PreferencesModal: React.FC = ({ preferences, setPreferences, onClose, isMobile }) => { - const [activeTab, setActiveTab] = useState('general'); +const PreferencesModal: React.FC = ({ + preferences, + setPreferences, + onClose, + isMobile, +}) => { + const [activeTab, setActiveTab] = useState("general"); const openGitHub = () => { - window.open('https://github.com', '_blank'); + window.open("https://github.com", "_blank"); }; return (
{/* Adjusted modal dimensions and styling for mobile responsiveness */} -
+

⚙️ Preferences

- +
{/* Responsive sidebar width and content */} -
- {['general', 'shortcuts', 'appearance', 'about'].map(tab => ( +
+ {["general", "shortcuts", "appearance", "about"].map((tab) => ( ))}
- {activeTab === 'general' && ( + {activeTab === "general" && (

Focus Soundscapes

-

Enable procedural ambient noise during work sessions.

+

+ Enable procedural ambient noise during work sessions. +

- + setPreferences({ + ...preferences, + accentColor: e.target.value, + }) + } + className="w-10 h-8 rounded-lg bg-transparent border border-slate-700 cursor-pointer" + /> +
+
+ +
+ +
+

+ Desktop Color Theme +

+
{[ - { id: 'nebula', label: 'Nebula', icon: '🌌', gradient: "from-blue-500 via-indigo-500 to-pink-500", desc: "Deep space blues, purples, and cosmic pinks" }, - { id: 'sunrise', label: 'Sunrise', icon: '🌅', gradient: "from-orange-500 via-red-500 to-pink-500", desc: "Warm oranges, radiant reds, and golden embers" }, - { id: 'ocean', label: 'Ocean Depth', icon: '🌊', gradient: "from-cyan-500 via-teal-500 to-blue-500", desc: "Cool cyans, deep marine teals, and light blues" }, - { id: 'emerald', label: 'Emerald Forest', icon: '🌲', gradient: "from-emerald-500 via-green-500 to-lime-500", desc: "Lush greens, forest emeralds, and lime details" }, - ].map(theme => ( + { + id: "nebula", + label: "Nebula", + icon: "🌌", + gradient: "from-blue-500 via-indigo-500 to-pink-500", + desc: "Deep space blues, purples, and cosmic pinks", + }, + { + id: "sunrise", + label: "Sunrise", + icon: "🌅", + gradient: "from-orange-500 via-red-500 to-pink-500", + desc: "Warm oranges, radiant reds, and golden embers", + }, + { + id: "ocean", + label: "Ocean Depth", + icon: "🌊", + gradient: "from-cyan-500 via-teal-500 to-blue-500", + desc: "Cool cyans, deep marine teals, and light blues", + }, + { + id: "emerald", + label: "Emerald Forest", + icon: "🌲", + gradient: "from-emerald-500 via-green-500 to-lime-500", + desc: "Lush greens, forest emeralds, and lime details", + }, + ].map((theme) => ( ))}
@@ -127,19 +282,34 @@ const PreferencesModal: React.FC = ({ preferences, setPreferences, onClos
-

Pin indicator style

+

+ Pin indicator style +

{/* Grid layout adjustment for mobile */} -
+
{[ - { id: IndicatorStyle.SUBTLE_BORDER, label: 'Subtle Border' }, - { id: IndicatorStyle.COLORED_GLOW, label: 'Colored Glow' }, - { id: IndicatorStyle.PIN_ICON, label: 'Pin Icon' }, - { id: IndicatorStyle.NONE, label: 'None' }, - ].map(style => ( + { + id: IndicatorStyle.SUBTLE_BORDER, + label: "Subtle Border", + }, + { + id: IndicatorStyle.COLORED_GLOW, + label: "Colored Glow", + }, + { id: IndicatorStyle.PIN_ICON, label: "Pin Icon" }, + { id: IndicatorStyle.NONE, label: "None" }, + ].map((style) => ( @@ -149,35 +319,44 @@ const PreferencesModal: React.FC = ({ preferences, setPreferences, onClos
)} - {activeTab === 'about' && ( + {activeTab === "about" && (

PinPoint Pro

-

Version 1.0.0-beta

+

+ Version 1.0.0-beta +

- +
-

Open Source

+

+ Open Source +

- PinPoint Pro is proudly open source. Built for deep work, this tool belongs to the community. + PinPoint Pro is proudly open source. Built for deep work, + this tool belongs to the community.

-

License

+

+ License +

- MIT License
+ MIT License
Copyright (c) 2025 PinPoint Contributors

-
@@ -187,7 +366,12 @@ const PreferencesModal: React.FC = ({ preferences, setPreferences, onClos
- +
diff --git a/StatusBar.tsx b/StatusBar.tsx index f1e1c9c..591c9bd 100644 --- a/StatusBar.tsx +++ b/StatusBar.tsx @@ -1,12 +1,24 @@ - -import React, { useState } from 'react'; -import { ColorTheme, FocusTimerState, TabData, SoundscapeType, WindowState } from './types'; -import { Pin } from 'lucide-react'; +import React, { useState } from "react"; +import { + ColorTheme, + FocusTimerState, + TabData, + SoundscapeType, + WindowState, + AppTheme, +} from "./types"; +import { Pin } from "lucide-react"; +import ThemeToggle from "./ThemeToggle"; +import ColorStylePicker from "./ColorStylePicker"; interface Props { pinnedCount: number; focusMode: boolean; currentTheme: ColorTheme; + theme: AppTheme; + onToggleTheme: () => void; + accentColor: string; + onAccentChange: (color: string) => void; focusTimer: FocusTimerState; archivedTabs?: TabData[]; onRestoreTab?: (tab: TabData) => void; @@ -26,25 +38,29 @@ interface Props { onRestoreWindow?: (id: string) => void; } -const THEMES: ColorTheme[] = ['nebula', 'sunrise', 'ocean', 'emerald']; +const THEMES: ColorTheme[] = ["nebula", "sunrise", "ocean", "emerald"]; const THEME_ICONS: Record = { - nebula: '🌌', - sunrise: '🌅', - ocean: '🌊', - emerald: '🌲', + nebula: "🌌", + sunrise: "🌅", + ocean: "🌊", + emerald: "🌲", }; -const StatusBar: React.FC = ({ - pinnedCount, - focusMode, - currentTheme, +const StatusBar: React.FC = ({ + pinnedCount, + focusMode, + currentTheme, + theme, + onToggleTheme, + accentColor, + onAccentChange, focusTimer, archivedTabs = [], onRestoreTab, onToggleFocusTimer, - onThemeChange, - onOpenPrefs, - onOpenWorkspaces, + onThemeChange, + onOpenPrefs, + onOpenWorkspaces, onOpenAssistant, onOpenSearch, onOpenEcho, @@ -54,7 +70,7 @@ const StatusBar: React.FC = ({ soundscapeVolume, onSoundscapeVolumeChange, closedWindows = [], - onRestoreWindow + onRestoreWindow, }) => { const [isVaultOpen, setIsVaultOpen] = useState(false); const [isSoundscapeMenuOpen, setIsSoundscapeMenuOpen] = useState(false); @@ -63,7 +79,7 @@ const StatusBar: React.FC = ({ const formatTime = (seconds: number) => { const m = Math.floor(seconds / 60); const s = seconds % 60; - return `${m}:${s.toString().padStart(2, '0')}`; + return `${m}:${s.toString().padStart(2, "0")}`; }; const cycleTheme = () => { @@ -72,7 +88,7 @@ const StatusBar: React.FC = ({ onThemeChange(THEMES[nextIndex]); }; - const containerClasses = isMobile + const containerClasses = isMobile ? "fixed bottom-0 left-0 right-0 z-[100] flex items-center justify-around px-2 py-4 glass border-t border-white/10" : "fixed top-4 left-1/2 -translate-x-1/2 z-[100] flex items-center space-x-2 px-3 py-1.5 glass rounded-full shadow-2xl transition-all duration-500 hover:py-2"; @@ -82,44 +98,67 @@ const StatusBar: React.FC = ({ <>
- PinPoint + + PinPoint +
)} + + + {/* Theme Selector Control */}
- {isThemeMenuOpen && (
-

Aesthetic Themes

- Ambient +

+ Aesthetic Themes +

+ + Ambient +
- {THEMES.map(theme => { + {THEMES.map((theme) => { const isActive = currentTheme === theme; let gradientClass = ""; - if (theme === 'nebula') gradientClass = "from-blue-500 via-indigo-500 to-pink-500"; - else if (theme === 'sunrise') gradientClass = "from-orange-500 via-red-500 to-pink-500"; - else if (theme === 'ocean') gradientClass = "from-cyan-500 via-teal-500 to-blue-500"; - else if (theme === 'emerald') gradientClass = "from-emerald-500 via-green-500 to-lime-500"; + if (theme === "nebula") + gradientClass = "from-blue-500 via-indigo-500 to-pink-500"; + else if (theme === "sunrise") + gradientClass = "from-orange-500 via-red-500 to-pink-500"; + else if (theme === "ocean") + gradientClass = "from-cyan-500 via-teal-500 to-blue-500"; + else if (theme === "emerald") + gradientClass = "from-emerald-500 via-green-500 to-lime-500"; return ( ); @@ -147,7 +192,7 @@ const StatusBar: React.FC = ({ {/* Soundscape Control */}
- {isSoundscapeMenuOpen && (
-

Soundscape

- {soundscape !== 'none' && ( +

+ Soundscape +

+ {soundscape !== "none" && (
- Playing + + Playing +
)}
@@ -181,17 +239,17 @@ const StatusBar: React.FC = ({
{[ - { id: 'none', label: 'Mute', icon: '🔇' }, - { id: 'ambient', label: 'Ambient', icon: '🌌' }, - { id: 'lofi', label: 'Lofi Keys', icon: '☕' }, - { id: 'nature', label: 'Rain/Wind', icon: '🌲' } - ].map(item => ( + { id: "none", label: "Mute", icon: "🔇" }, + { id: "ambient", label: "Ambient", icon: "🌌" }, + { id: "lofi", label: "Lofi Keys", icon: "☕" }, + { id: "nature", label: "Rain/Wind", icon: "🌲" }, + ].map((item) => (
{/* Volume Slider */} - {soundscape !== 'none' && ( + {soundscape !== "none" && (
Volume @@ -213,7 +271,9 @@ const StatusBar: React.FC = ({ max="1" step="0.05" value={soundscapeVolume} - onChange={(e) => onSoundscapeVolumeChange(parseFloat(e.target.value))} + onChange={(e) => + onSoundscapeVolumeChange(parseFloat(e.target.value)) + } className="w-full h-1 bg-slate-800 rounded-lg appearance-none cursor-pointer accent-blue-500" />
@@ -227,9 +287,9 @@ const StatusBar: React.FC = ({ {/* The Vault Button */}
-
))}
- {archivedTabs.length > 0 &&
} + {archivedTabs.length > 0 && ( +
+ )}
)} {/* Section 2: Archived Tabs */}
-

Surface Archive

- {archivedTabs.length} Resting +

+ Surface Archive +

+ + {archivedTabs.length} Resting +
- +
{archivedTabs.length === 0 && closedWindows.length === 0 ? ( -

The vault is currently empty.

+

+ The vault is currently empty. +

) : archivedTabs.length === 0 ? ( -

No archived browser tabs.

+

+ No archived browser tabs. +

) : ( - archivedTabs.map(tab => ( -
+ archivedTabs.map((tab) => ( +
-

{tab.title}

-

{tab.url}

+

+ {tab.title} +

+

+ {tab.url} +

-
- - - - {!isMobile && ( - )} - {pinnedCount > 0 && ( diff --git a/ThemeToggle.tsx b/ThemeToggle.tsx new file mode 100644 index 0000000..233fea1 --- /dev/null +++ b/ThemeToggle.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import { AppTheme } from "./types"; +import { Sun, Moon } from "lucide-react"; + +interface Props { + theme: AppTheme; + onToggle: () => void; + isMobile?: boolean; +} + +const ThemeToggle: React.FC = ({ theme, onToggle, isMobile }) => { + const isDark = theme === "dark"; + return ( + + ); +}; + +export default ThemeToggle; diff --git a/index.html b/index.html index 965f5a1..ced3d87 100644 --- a/index.html +++ b/index.html @@ -1,17 +1,31 @@ - - + - + PinPoint Pro - - - + + +
- - + + diff --git a/package-lock.json b/package-lock.json index 2b61031..d9db22f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -302,10 +302,435 @@ "node": ">=6.9.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/linux-x64": { "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], @@ -313,7 +738,7 @@ "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], "engines": { "node": ">=18" @@ -457,6 +882,244 @@ "dev": true, "license": "MIT" }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.62.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", @@ -485,6 +1148,90 @@ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -603,9 +1350,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.38", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz", - "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==", + "version": "2.11.21", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", + "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -625,9 +1372,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", - "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", + "version": "4.28.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", + "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", "dev": true, "funding": [ { @@ -645,11 +1392,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.38", - "caniuse-lite": "^1.0.30001799", - "electron-to-chromium": "^1.5.376", - "node-releases": "^2.0.48", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" }, "bin": { "browserslist": "cli.js" @@ -665,9 +1412,9 @@ "license": "BSD-3-Clause" }, "node_modules/caniuse-lite": { - "version": "1.0.30001799", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", - "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", "dev": true, "funding": [ { @@ -728,9 +1475,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.378", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.378.tgz", - "integrity": "sha512-VinvOAuuPmdD1guEgGv5f2Qp7/vlfqOrUOMYNnOD4wj3pit8kRsQHzfIf6teyUGWo15Tg5+bOJaRunvyltpVWQ==", + "version": "1.5.425", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.425.tgz", + "integrity": "sha512-QvPtl41EUOnuT1HBvMKgxXRIaHNcagBPs50u7VULzhZXaGfqTbZyE16LQsctZ/RQHlGu+FOWeDTR4mY6YbeF1g==", "dev": true, "license": "ISC" }, @@ -845,6 +1592,21 @@ "node": ">=12.20.0" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/gaxios": { "version": "7.1.5", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.5.tgz", @@ -1017,9 +1779,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "dev": true, "funding": [ { @@ -1074,9 +1836,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.49", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.49.tgz", - "integrity": "sha512-f06bl1D+8ZDkn2oOQQKAh5/otFWqVnM1Q5oerA8Pex7UfT66Tx4IPHIqVVFKqFT3FUtaDstdgkM7yT7JWhqxfw==", + "version": "2.0.55", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.55.tgz", + "integrity": "sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==", "dev": true, "license": "MIT", "engines": { @@ -1117,9 +1879,9 @@ } }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "dev": true, "funding": [ { @@ -1137,7 +1899,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -1146,9 +1908,9 @@ } }, "node_modules/protobufjs": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz", - "integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz", + "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -1337,9 +2099,9 @@ "license": "MIT" }, "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", + "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", "dev": true, "funding": [ { diff --git a/types.ts b/types.ts index 037d64a..e58d5b3 100644 --- a/types.ts +++ b/types.ts @@ -1,14 +1,23 @@ - export enum IndicatorStyle { - SUBTLE_BORDER = 'subtle_border', - COLORED_GLOW = 'colored_glow', - PIN_ICON = 'pin_icon', - NONE = 'none' + SUBTLE_BORDER = "subtle_border", + COLORED_GLOW = "colored_glow", + PIN_ICON = "pin_icon", + NONE = "none", } -export type ColorTheme = 'nebula' | 'sunrise' | 'ocean' | 'emerald'; -export type Priority = 'critical' | 'high' | 'medium' | 'low'; -export type SoundscapeType = 'none' | 'lofi' | 'ambient' | 'nature'; +export type ColorTheme = "nebula" | "sunrise" | "ocean" | "emerald"; +export type Priority = "critical" | "high" | "medium" | "low"; +export type SoundscapeType = "none" | "lofi" | "ambient" | "nature"; +export type AppTheme = "light" | "dark"; + +export const ACCENT_PRESETS: { name: string; value: string }[] = [ + { name: "Azure", value: "#3b82f6" }, + { name: "Violet", value: "#8b5cf6" }, + { name: "Rose", value: "#f43f5e" }, + { name: "Amber", value: "#f59e0b" }, + { name: "Emerald", value: "#10b981" }, + { name: "Cyan", value: "#06b6d4" }, +]; export interface TodoItem { id: string; @@ -30,7 +39,15 @@ export interface TabData { export interface WindowState { id: string; title: string; - type: 'browser' | 'code' | 'chat' | 'media' | 'pdf' | 'notes' | 'folder' | 'canvas'; + type: + | "browser" + | "code" + | "chat" + | "media" + | "pdf" + | "notes" + | "folder" + | "canvas"; x: number; y: number; width: number; @@ -41,8 +58,8 @@ export interface WindowState { isCollapsed?: boolean; focusCount?: number; preSnapDimensions?: { x: number; y: number; width: number; height: number }; - timerTotal?: number; - timerRemaining?: number; + timerTotal?: number; + timerRemaining?: number; content: string; isGhosted?: boolean; tabs?: TabData[]; @@ -64,8 +81,8 @@ export interface Workspace { export interface FocusTimerState { isActive: boolean; - timeLeft: number; - mode: 'focus' | 'break'; + timeLeft: number; + mode: "focus" | "break"; } export interface UserPreferences { @@ -73,6 +90,7 @@ export interface UserPreferences { showMenuBarIcon: boolean; indicatorStyle: IndicatorStyle; accentColor: string; + theme: AppTheme; minOpacity: number; zenMode: boolean; ghostingEnabled: boolean;