diff --git a/astro.config.mjs b/astro.config.mjs index 4585435..4204ccc 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -14,7 +14,7 @@ let target = process.env.VITE_API_URL || 'http://localhost:51015'; const apiProxy = { target: target, changeOrigin: true, ws: true, rewriteWsOrigin: true, secure: false, cookieDomainRewrite: 'localhost' }; let proxy = { '/root/': { - target: `${target}/root/`, + target: `${target}`, }, '/api': apiProxy, '/client': apiProxy, diff --git a/package.json b/package.json index 3d80c6c..e41664e 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,18 @@ { - "name": "@kevisual/astro-simplate-template", + "name": "@kevisual/hot-api", "version": "0.0.2", "description": "", "main": "index.js", - "basename": "/root/astro-simplate-template-docs", + "basename": "/root/hot-api", "scripts": { "dev": "astro dev", "build": "astro build", "preview": "astro preview", - "pub": "envision deploy ./dist -k astro-simplate-template-docs -v 0.0.2 -u", + "pub": "envision deploy ./dist -k hot-api -v 0.0.2 -u -y y", + "pub:docs": "envision deploy ./dist -k hot-api-docs -v 0.0.2 -u", "slide:dev": "slidev --open slides/index.md", - "slide:build": "slidev build slides/index.md --base /root/astro-simplate-template-slide/", - "slide:pub": "envision deploy ./slides/dist -k astro-simplate-template-slide -v 0.0.2 -u", + "slide:build": "slidev build slides/index.md --base /root/hot-api-slide/", + "slide:pub": "envision deploy ./slides/dist -k hot-api-slide -v 0.0.2 -u", "ui": "pnpm dlx shadcn@latest add " }, "keywords": [], diff --git a/src/apps/bg.tsx b/src/apps/bg.tsx new file mode 100644 index 0000000..1a1f105 --- /dev/null +++ b/src/apps/bg.tsx @@ -0,0 +1,18 @@ + +export const BG = (props: { children: React.ReactNode }) => { + return ( +
+ {props.children} +
+ ); +} \ No newline at end of file diff --git a/src/apps/hotkeys/components/icon.tsx b/src/apps/hotkeys/components/icon.tsx new file mode 100644 index 0000000..c0771cf --- /dev/null +++ b/src/apps/hotkeys/components/icon.tsx @@ -0,0 +1,60 @@ +import { useStore } from "../store"; +export const RefreshButton = () => { + const { isLoading, fetchItems } = useStore(); + + return ( + + ); +}; + +export const SettingsButton = () => { + return ( + + ); +}; \ No newline at end of file diff --git a/src/apps/hotkeys/index.tsx b/src/apps/hotkeys/index.tsx new file mode 100644 index 0000000..b110f2f --- /dev/null +++ b/src/apps/hotkeys/index.tsx @@ -0,0 +1,187 @@ +/** + * title: Hotkeys App Component + * description: A React component displaying a grid of glassmorphism cards with icons or abbreviated titles, using Zustand for state management and Tailwind CSS for styling. Includes an advanced search feature with glassmorphism and hover effects. + * tags: react, zustand, tailwindcss, glassmorphism, component, search + * createdAt: 2025-12-05 + */ + +import { useEffect, useState } from 'react'; +import { SettingsButton, RefreshButton } from './components/icon'; +import { useStore, CardItem } from './store'; +const Card = ({ item }: { item: CardItem }) => { + const [isPressed, setIsPressed] = useState(false); + const { sendEvent } = useStore(); + // 动效逻辑:点击后动一下恢复 + const handleClick = () => { + setIsPressed(true); + setTimeout(() => setIsPressed(false), 150); + sendEvent(item); + }; + + return ( +
+ {/* Tech Glow Lines */} +
+
+
+
+
+
+ + {/* Shine effect on hover */} +
+ + {/* Content Container */} +
+ {/* Icon or Title Placeholder */} +
+ {item.iconUrl ? ( + {item.title} { + // Fallback if image fails to load: hide image and show text + e.currentTarget.style.display = 'none'; + const parent = e.currentTarget.parentElement; + if (parent) { + const span = document.createElement('span'); + span.innerText = item.title.slice(0, 2); + parent.appendChild(span); + } + }} + /> + ) : ( + {item.title.slice(0, 2)} + )} +
+ + {/* Full Title (Truncated if too long) */} +
+

+ {item.title} +

+
+
+
+ ); +}; + + + +const AdvancedSearch = () => { + const [isFocused, setIsFocused] = useState(false); + + const handleSearch = () => { + // 搜索功能预留 + console.log("Search triggered"); + }; + + return ( +
{ + setIsFocused(true) + }} + > + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + onKeyDown={(e) => e.key === 'Enter' && handleSearch()} + /> + + +
+ ); +}; + +export const App = () => { + const { items, isLoading, fetchItems } = useStore(); + + useEffect(() => { + fetchItems(); + }, [fetchItems]); + + return ( +
+ {/* Overlay for premium look and contrast */} +
+ +
+
+

+ + HotKeys + + {/* 3D层次感背景文字 */} + + {/* 发光边缘效果 */} +

+
+ + + +
+
+ + {isLoading ? ( +
+
+
+
+
+
+ ) : ( +
+ {items.map((item) => ( + + ))} +
+ )} +
+
+ ); +}; \ No newline at end of file diff --git a/src/apps/hotkeys/store.ts b/src/apps/hotkeys/store.ts new file mode 100644 index 0000000..e2a9872 --- /dev/null +++ b/src/apps/hotkeys/store.ts @@ -0,0 +1,58 @@ +import { create } from 'zustand'; +import { query } from '../../modules/query' +// --- Types --- +export interface CardItem { + id: string; + title: string; + iconUrl?: string; + description?: string; + data?: any; +} + +export interface StoreState { + items: CardItem[]; + isLoading: boolean; + fetchItems: () => Promise; + sendEvent: (item: CardItem) => Promise; +} + +// --- Store --- +export const useStore = create((set) => ({ + items: [], + isLoading: false, + fetchItems: async () => { + set({ isLoading: true }); + // TODO: Replace with actual API call + // const response = await fetch('/api/hotkeys'); + // const data = await response.json(); + + // Mock data for demonstration + const mockData: CardItem[] = Array.from({ length: 12 }).map((_, i) => ({ + id: `item-${i}`, + title: i % 4 === 0 ? `工具 ${i + 1}` : `Application Long Name ${i + 1}`, + iconUrl: i % 3 === 0 ? `https://api.dicebear.com/7.x/icons/svg?seed=${i}` : undefined, + description: `Description for item ${i + 1}` + })); + mockData.unshift({ + id: 'item-search', + title: 'win+d 显示桌面', + iconUrl: 'https://api.dicebear.com/7.x/icons/svg?seed=search', + description: '显示桌面' + }) + // Simulate network delay + await new Promise(resolve => setTimeout(resolve, 800)); + + set({ items: mockData, isLoading: false }); + }, + sendEvent: async (item: CardItem) => { + // client/router?path=key-sender&keys=win+d + const res = await query.post({ + path: 'key-sender', + keys: 'win+d' + }); + console.log('Event sent for item:', item, 'Response:', res); + if (res.code !== 200) { + alert('Failed to send event'); + } + } +})); diff --git a/src/modules/query.ts b/src/modules/query.ts index 22b6bd1..4b7d90a 100644 --- a/src/modules/query.ts +++ b/src/modules/query.ts @@ -1,4 +1,4 @@ -import { Query } from '@kevisual/query' +import { QueryClient } from '@kevisual/query' const getUrl = () => { const host = window.location.host @@ -10,6 +10,6 @@ const getUrl = () => { return '/client/router' } -export const query = new Query({ - url: getUrl() +export const query = new QueryClient({ + url: 'http://localhost:51015/client/router', }); \ No newline at end of file diff --git a/src/pages/index.astro b/src/pages/index.astro index 3e848df..a08c587 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,47 +1,10 @@ --- -// import { query } from '@/modules/query.ts'; -console.log('Hello from index.astro'); -import '../styles/global.css'; +import Html from '@/components/html.astro'; +import { App } from '../apps/hotkeys/index.tsx'; --- - - - My Homepage - - -

Welcome to my website!

-
-
- - - - + +
+ +
+