diff --git a/frontend/package.json b/frontend/package.json index 1ca3351d..365188d0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -48,6 +48,7 @@ "@radix-ui/react-use-controllable-state": "^1.2.2", "@t3-oss/env-nextjs": "^0.12.0", "@tanstack/react-query": "^5.90.17", + "@tombcato/smart-ticker": "^1.2.4", "@types/hast": "^3.0.4", "@uiw/codemirror-theme-basic": "^4.25.4", "@uiw/codemirror-theme-monokai": "^4.25.4", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index a7a3ec81..5fbe4a45 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -92,6 +92,9 @@ importers: '@tanstack/react-query': specifier: ^5.90.17 version: 5.90.20(react@19.2.4) + '@tombcato/smart-ticker': + specifier: ^1.2.4 + version: 1.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vue@3.5.28(typescript@5.9.3)) '@types/hast': specifier: ^3.0.4 version: 3.0.4 @@ -2030,6 +2033,9 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@stackblitz/sdk@1.11.0': + resolution: {integrity: sha512-DFQGANNkEZRzFk1/rDP6TcFdM82ycHE+zfl9C/M/jXlH68jiqHWHFMQURLELoD8koxvu/eW5uhg94NSAZlYrUQ==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -2189,6 +2195,23 @@ packages: '@tokenlens/models@1.3.0': resolution: {integrity: sha512-9mx7ZGeewW4ndXAiD7AT1bbCk4OpJeortbjHHyNkgap+pMPPn1chY6R5zqe1ggXIUzZ2l8VOAKfPqOvpcrisJw==} + '@tombcato/smart-ticker@1.2.4': + resolution: {integrity: sha512-AJDoqvcwZ7MqEjkR8teGqkxXFmI0M35k4fdoDjCuTEhVYpryzequtZLO8Zmz2wvKSBKkZ8+LOWbVgBHivsZI9Q==} + peerDependencies: + react: '>=17.0.0' + react-dom: '>=17.0.0' + svelte: '>=4.0.0' + vue: '>=3.0.0' + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + svelte: + optional: true + vue: + optional: true + '@ts-morph/common@0.28.1': resolution: {integrity: sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==} @@ -7783,6 +7806,8 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@stackblitz/sdk@1.11.0': {} + '@standard-schema/spec@1.1.0': {} '@swc/helpers@0.5.15': @@ -7911,6 +7936,14 @@ snapshots: dependencies: '@tokenlens/core': 1.3.0 + '@tombcato/smart-ticker@1.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vue@3.5.28(typescript@5.9.3))': + dependencies: + '@stackblitz/sdk': 1.11.0 + optionalDependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + vue: 3.5.28(typescript@5.9.3) + '@ts-morph/common@0.28.1': dependencies: minimatch: 10.2.5 diff --git a/frontend/src/app/workspace/chats/[thread_id]/motivation-slogans.json b/frontend/src/app/workspace/chats/[thread_id]/motivation-slogans.json new file mode 100644 index 00000000..79ff7a6b --- /dev/null +++ b/frontend/src/app/workspace/chats/[thread_id]/motivation-slogans.json @@ -0,0 +1,42 @@ +[ + { + "text": "开工!摸鱼退散🐟💨", + "color": "#FF6B6B" + }, + { + "text": "学习搞起,摆烂禁止🙅‍♂️", + "color": "#4ECDC4" + }, + { + "text": "卷不动也得动💪", + "color": "#45B7D1" + }, + { + "text": "搬砖学习,同步上线🧱", + "color": "#96CEB4" + }, + { + "text": "别躺了,搞钱要紧💰", + "color": "#FFA559" + }, + { + "text": "今日份努力已上线✨", + "color": "#A78BFA" + }, + { + "text": "支棱起来,干活啦🚀", + "color": "#FF9F1C" + }, + { + "text": "拒绝摆烂,从我做起😤", + "color": "#2EC4B6" + }, + { + "text": "学习人,不犯困😪", + "color": "#E71D36" + }, + { + "text": "冲冲冲,别摸鱼🐎", + "color": "#3A86FF" + } +] diff --git a/frontend/src/app/workspace/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/chats/[thread_id]/page.tsx index b3df477e..8fb9a478 100644 --- a/frontend/src/app/workspace/chats/[thread_id]/page.tsx +++ b/frontend/src/app/workspace/chats/[thread_id]/page.tsx @@ -40,10 +40,14 @@ import { env } from "@/env"; import { useSelectedSkillListener } from "@/hooks/use-selected-skill-listener"; import { cn } from "@/lib/utils"; import { IframeTestPanel } from "@/components/workspace/iframe-test-panel"; +import { Ticker } from "@tombcato/smart-ticker"; +import "@tombcato/smart-ticker/style.css"; +import motivationSlogans from "./motivation-slogans.json"; export default function ChatPage() { const { t } = useI18n(); useSpecificChatMode(); + const [sloganIndex, setSloganIndex] = useState(0); const [settings, setSettings] = useLocalSettings(); const { setOpen: setSidebarOpen } = useSidebar(); const router = useRouter(); @@ -92,6 +96,35 @@ export default function ChatPage() { const initializedThreadRef = useRef(null); const { showNotification } = useNotification(); + const currentSlogan = + motivationSlogans[sloganIndex % motivationSlogans.length] ?? { + text: "来,一起学习工作吧", + color: "#333333", + }; + const tickerCharacterList = useMemo(() => { + const seen = new Set(); + const uniqueChars: string[] = []; + + for (const slogan of motivationSlogans) { + for (const char of slogan.text) { + if (seen.has(char)) continue; + seen.add(char); + uniqueChars.push(char); + } + } + + return uniqueChars.join(""); + }, []); + + useEffect(() => { + if (motivationSlogans.length <= 1) return; + + const timer = window.setInterval(() => { + setSloganIndex((prev) => (prev + 1) % motivationSlogans.length); + }, 10 * 60 * 1000); + + return () => window.clearInterval(timer); + }, []); useEffect(() => { if (!isNewThread) { @@ -329,10 +362,22 @@ export default function ChatPage() { -
+
{/* threadTitle={title} */} {title !== "Untitled" && ( - + // + )}