From 85acbe5bc4239308cd19c5158cdae6df5c3da25d Mon Sep 17 00:00:00 2001 From: ClanEver <562211524@qq.com> Date: Wed, 8 Jan 2025 14:03:13 +0800 Subject: [PATCH] tmp --- .prettierrc | 3 +- app/layout.tsx | 48 +++--- common/{game_rules.ts => gameRules.ts} | 0 common/icons.tsx | 19 +++ common/lang.ts | 126 +++++++++++++++ common/themes.ts | 34 ++++ components/CollaborativeEditor.tsx | 208 +++++-------------------- components/FloatingMenu.tsx | 59 +++++++ components/SettingPanel.tsx | 73 +++------ components/ThemeItem.tsx | 59 +++++++ components/ThemePanel.tsx | 73 +++++++++ package.json | 2 + pnpm-lock.yaml | 17 ++ run.fish | 10 ++ tailwind.config.ts | 2 + 15 files changed, 493 insertions(+), 240 deletions(-) rename common/{game_rules.ts => gameRules.ts} (100%) create mode 100644 common/icons.tsx create mode 100644 common/lang.ts create mode 100644 common/themes.ts create mode 100644 components/FloatingMenu.tsx create mode 100644 components/ThemeItem.tsx create mode 100644 components/ThemePanel.tsx create mode 100644 run.fish diff --git a/.prettierrc b/.prettierrc index b2095be..ef2ab10 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,5 @@ { "semi": false, - "singleQuote": true + "singleQuote": true, + "tabWidth": 4 } diff --git a/app/layout.tsx b/app/layout.tsx index f7fa87e..15e196b 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,34 +1,36 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; +import { FloatingMenu } from '@/components/FloatingMenu' +import type { Metadata } from 'next' +import { Geist, Geist_Mono } from 'next/font/google' +import './globals.css' const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); + variable: '--font-geist-sans', + subsets: ['latin'] +}) const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); + variable: '--font-geist-mono', + subsets: ['latin'] +}) export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", -}; + title: 'Create Next App', + description: 'Generated by create next app' +} export default function RootLayout({ - children, + children }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode; }>) { - return ( - - - {children} - - - ); + return ( + + + { children } + + + + ) } diff --git a/common/game_rules.ts b/common/gameRules.ts similarity index 100% rename from common/game_rules.ts rename to common/gameRules.ts diff --git a/common/icons.tsx b/common/icons.tsx new file mode 100644 index 0000000..92c19f6 --- /dev/null +++ b/common/icons.tsx @@ -0,0 +1,19 @@ +export const Icons = { + Settings: (props: React.ComponentProps<'svg'>) => ( + + + + ), +} diff --git a/common/lang.ts b/common/lang.ts new file mode 100644 index 0000000..0d4cf2c --- /dev/null +++ b/common/lang.ts @@ -0,0 +1,126 @@ +import { StreamLanguage } from '@codemirror/language' + +export const languages = [ + { + name: 'Python', + extension: () => + import('@codemirror/legacy-modes/mode/python').then(({ python }) => + StreamLanguage.define(python) + ) + }, + { + name: 'C#', + extension: () => + import('@codemirror/legacy-modes/mode/clike').then(({ csharp }) => + StreamLanguage.define(csharp) + ) + }, + { + name: 'C++', + extension: () => + import('@codemirror/legacy-modes/mode/clike').then(({ cpp }) => + StreamLanguage.define(cpp) + ) + }, + { + name: 'C', + extension: () => + import('@codemirror/legacy-modes/mode/clike').then(({ c }) => + StreamLanguage.define(c) + ) + }, + { + name: 'JavaScript', + extension: () => + import('@codemirror/legacy-modes/mode/javascript').then( + ({ javascript }) => StreamLanguage.define(javascript) + ) + }, + { + name: 'TypeScript', + extension: () => + import('@codemirror/legacy-modes/mode/javascript').then( + ({ typescript }) => StreamLanguage.define(typescript) + ) + }, + { + name: 'Kotlin', + extension: () => + import('@codemirror/legacy-modes/mode/clike').then(({ kotlin }) => + StreamLanguage.define(kotlin) + ) + }, + { + name: 'Java', + extension: () => + import('@codemirror/legacy-modes/mode/clike').then(({ java }) => + StreamLanguage.define(java) + ) + }, + { + name: 'PHP', + extension: () => + import('@codemirror/lang-php').then(({ php }) => php()) + }, + { + name: 'Swift', + extension: () => + import('@codemirror/legacy-modes/mode/swift').then(({ swift }) => + StreamLanguage.define(swift) + ) + }, + { + name: 'Dart', + extension: () => + import('@codemirror/legacy-modes/mode/clike').then(({ dart }) => + StreamLanguage.define(dart) + ) + }, + { + name: 'Go', + extension: () => + import('@codemirror/legacy-modes/mode/go').then(({ go }) => + StreamLanguage.define(go) + ) + }, + { + name: 'Ruby', + extension: () => + import('@codemirror/legacy-modes/mode/ruby').then(({ ruby }) => + StreamLanguage.define(ruby) + ) + }, + { + name: 'Scala', + extension: () => + import('@codemirror/legacy-modes/mode/clike').then(({ scala }) => + StreamLanguage.define(scala) + ) + }, + { + name: 'Rust', + extension: () => + import('@codemirror/legacy-modes/mode/rust').then(({ rust }) => + StreamLanguage.define(rust) + ) + }, + { + name: 'Racket', + extension: () => + import('@codemirror/legacy-modes/mode/scheme').then(({ scheme }) => + StreamLanguage.define(scheme) + ) + }, + { + name: 'Erlang', + extension: () => + import('@codemirror/legacy-modes/mode/erlang').then(({ erlang }) => + StreamLanguage.define(erlang) + ) + }, + { + name: 'Elixir', + extension: () => + import('codemirror-lang-elixir').then(({ elixir }) => elixir()) + } +] \ No newline at end of file diff --git a/common/themes.ts b/common/themes.ts new file mode 100644 index 0000000..63321d8 --- /dev/null +++ b/common/themes.ts @@ -0,0 +1,34 @@ +export default themes = [ + 'cupcake', + 'dark', + 'light', + 'bumblebee', + 'emerald', + 'corporate', + 'synthwave', + 'retro', + 'cyberpunk', + 'valentine', + 'halloween', + 'garden', + 'forest', + 'aqua', + 'lofi', + 'pastel', + 'fantasy', + 'wireframe', + 'black', + 'luxury', + 'dracula', + 'cmyk', + 'autumn', + 'business', + 'acid', + 'lemonade', + 'night', + 'coffee', + 'winter', + 'dim', + 'nord', + 'sunset', +] diff --git a/components/CollaborativeEditor.tsx b/components/CollaborativeEditor.tsx index d18d41b..64885e3 100644 --- a/components/CollaborativeEditor.tsx +++ b/components/CollaborativeEditor.tsx @@ -1,5 +1,6 @@ 'use client' +import { languages } from '@/common/lang' import { useEffect, useRef, useState } from 'react' import { io, Socket } from 'socket.io-client' import CodeMirror, { @@ -7,9 +8,8 @@ import CodeMirror, { Extension, keymap, ReactCodeMirrorRef, - ViewUpdate, + ViewUpdate } from '@uiw/react-codemirror' -import { StreamLanguage } from '@codemirror/language' // import { vscodeDark } from '@uiw/codemirror-theme-vscode' import { insertNewlineAndIndent } from '@codemirror/commands' @@ -20,133 +20,9 @@ interface CollaborativeEditorProps { roomId: string | undefined } -const languages = [ - { - name: 'Python', - extension: () => - import('@codemirror/legacy-modes/mode/python').then(({ python }) => - StreamLanguage.define(python), - ), - }, - { - name: 'C#', - extension: () => - import('@codemirror/legacy-modes/mode/clike').then(({ csharp }) => - StreamLanguage.define(csharp), - ), - }, - { - name: 'C++', - extension: () => - import('@codemirror/legacy-modes/mode/clike').then(({ cpp }) => - StreamLanguage.define(cpp), - ), - }, - { - name: 'C', - extension: () => - import('@codemirror/legacy-modes/mode/clike').then(({ c }) => - StreamLanguage.define(c), - ), - }, - { - name: 'JavaScript', - extension: () => - import('@codemirror/legacy-modes/mode/javascript').then( - ({ javascript }) => StreamLanguage.define(javascript), - ), - }, - { - name: 'TypeScript', - extension: () => - import('@codemirror/legacy-modes/mode/javascript').then( - ({ typescript }) => StreamLanguage.define(typescript), - ), - }, - { - name: 'Kotlin', - extension: () => - import('@codemirror/legacy-modes/mode/clike').then(({ kotlin }) => - StreamLanguage.define(kotlin), - ), - }, - { - name: 'Java', - extension: () => - import('@codemirror/legacy-modes/mode/clike').then(({ java }) => - StreamLanguage.define(java), - ), - }, - { - name: 'PHP', - extension: () => - import('@codemirror/lang-php').then(({ php }) => php()), - }, - { - name: 'Swift', - extension: () => - import('@codemirror/legacy-modes/mode/swift').then(({ swift }) => - StreamLanguage.define(swift), - ), - }, - { - name: 'Dart', - extension: () => - import('@codemirror/legacy-modes/mode/clike').then(({ dart }) => - StreamLanguage.define(dart), - ), - }, - { - name: 'Go', - extension: () => - import('@codemirror/legacy-modes/mode/go').then(({ go }) => - StreamLanguage.define(go), - ), - }, - { - name: 'Ruby', - extension: () => - import('@codemirror/legacy-modes/mode/ruby').then(({ ruby }) => - StreamLanguage.define(ruby), - ), - }, - { - name: 'Scala', - extension: () => - import('@codemirror/legacy-modes/mode/clike').then(({ scala }) => - StreamLanguage.define(scala), - ), - }, - { - name: 'Rust', - extension: () => - import('@codemirror/legacy-modes/mode/rust').then(({ rust }) => - StreamLanguage.define(rust), - ), - }, - { - name: 'Racket', - extension: () => - import('@codemirror/legacy-modes/mode/scheme').then(({ scheme }) => - StreamLanguage.define(scheme), - ), - }, - { - name: 'Erlang', - extension: () => - import('@codemirror/legacy-modes/mode/erlang').then(({ erlang }) => - StreamLanguage.define(erlang), - ), - }, - { - name: 'Elixir', - extension: () => - import('codemirror-lang-elixir').then(({ elixir }) => elixir()), - }, -] export default function CollaborativeEditor({ - roomId, + roomId }: CollaborativeEditorProps) { const [editorContent, setEditorContent] = useState('') const [isReadOnly, setIsReadOnly] = useState(false) @@ -212,7 +88,7 @@ export default function CollaborativeEditor({ socketRef.current?.emit('contentChange', { roomId, content: value, - lastChar, + lastChar }) } } @@ -225,24 +101,24 @@ export default function CollaborativeEditor({ if (!roomId) { return ( -
-

Invalid room ID

+
+

Invalid room ID

) } if (!isJoined) { return ( -
-

Joining room {roomId}...

+
+

Joining room { roomId }...

) } if (error) { return ( -
-

{error}

+
+

{ error }

) } @@ -255,7 +131,7 @@ export default function CollaborativeEditor({ if (head !== docLength || anchor !== docLength) { view.dispatch({ - selection: { anchor: docLength, head: docLength }, + selection: { anchor: docLength, head: docLength } }) } } @@ -270,39 +146,39 @@ export default function CollaborativeEditor({ // 禁止粘贴 EditorView.domEventHandlers({ paste: () => true }), // 快捷键仅保留换行 - keymap.of([{ key: 'Enter', run: insertNewlineAndIndent }]), + keymap.of([{ key: 'Enter', run: insertNewlineAndIndent }]) ] return ( -
e.preventDefault()}> -
+
e.preventDefault() }> +
-
+

- You are User {userId} in Room {roomId}. - {isReadOnly - ? `User ${editingUser} is currently editing.` - : 'You can edit now. Press Enter to switch control.'} + You are User { userId } in Room { roomId }. + { isReadOnly + ? `User ${ editingUser } is currently editing.` + : 'You can edit now. Press Enter to switch control.' }

- {isReadOnly && ( + { isReadOnly && ( - )} + ) }
) diff --git a/components/FloatingMenu.tsx b/components/FloatingMenu.tsx new file mode 100644 index 0000000..2e9b0f9 --- /dev/null +++ b/components/FloatingMenu.tsx @@ -0,0 +1,59 @@ +'use client' + +import React, { useState } from 'react' +import { Button, Modal } from 'react-daisyui' +import SettingPanel from './SettingPanel' +// import ChangeThemePanel from './ChangeThemePanel'; +import { Icons } from '@/common/icons' +import { ThemePanel } from './ThemePanel' + +export const FloatingMenu: React.FC = ( + props: React.ComponentProps<'div'> | { className: string }, +) => { + // const [isSettingsOpen, setIsSettingsOpen] = useState(false) + // const [isThemeOpen, setIsThemeOpen] = useState(false) + const { Dialog: SettingDialog, handleShow: settingHandleShow } = + Modal.useDialog() + const { Dialog: ThemeDialog, handleShow: themeHandleShow } = + Modal.useDialog() + // const { , } = Modal.useDialog() + + return ( +
+ + + + 设置 + + + + +
+ +
+
+
+ + + + + 设置 + + + + +
+ +
+
+
+
+ ) +} diff --git a/components/SettingPanel.tsx b/components/SettingPanel.tsx index 5610c5f..6feefcb 100644 --- a/components/SettingPanel.tsx +++ b/components/SettingPanel.tsx @@ -1,7 +1,6 @@ import { Select, Card } from 'react-daisyui' import React from 'react' import { create } from 'zustand' - interface Settings { language: string theme: string @@ -9,12 +8,12 @@ interface Settings { interface SettingsStore { settings: Settings - updateSettings: (settings: Settings) => void + setSettings: (settings: Settings) => void } export const useSettingsStore = create((set) => ({ settings: { language: 'javascript', theme: 'light' }, - updateSettings: (newSettings) => set({ settings: newSettings }), + setSettings: (newSettings) => set({ settings: newSettings }), })) interface SettingPanelProps { @@ -24,59 +23,33 @@ interface SettingPanelProps { export default function SettingPanel({ onSettingChange = () => {}, }: SettingPanelProps) { - const { settings, updateSettings } = useSettingsStore() + const { settings, setSettings } = useSettingsStore() const handleSettingChange = (key: 'language' | 'theme', value: string) => { const newSettings = { ...settings, [key]: value } - updateSettings(newSettings) + setSettings(newSettings) onSettingChange(newSettings) } return ( - - -
- - -
- -
- - -
-
-
+
+
+ + +
+
) } diff --git a/components/ThemeItem.tsx b/components/ThemeItem.tsx new file mode 100644 index 0000000..d2b387b --- /dev/null +++ b/components/ThemeItem.tsx @@ -0,0 +1,59 @@ +import React from 'react' +import clsx from 'clsx' +import { twMerge } from 'tailwind-merge' + +export type ThemeItemProps = React.HTMLAttributes & { + dataTheme: string + selected?: boolean +} + +const ThemeItem = ({ + selected, + children, + dataTheme, + className, + ...props +}: ThemeItemProps) => { + const classes = twMerge( + className, + 'border-base-content/20 hover:border-base-content/40 outline-base-content\ + overflow-hidden rounded-lg border outline-2 outline-offset-2', + clsx({ + outline: selected, + }) + ) + + return ( +
+
+
+
+
+
+
{dataTheme}
+
+
+
A
+
+ +
+
A
+
+ +
+
A
+
+ +
+
A
+
+
+ {children &&
{children}
} +
+
+
+
+ ) +} + +export default ThemeItem diff --git a/components/ThemePanel.tsx b/components/ThemePanel.tsx new file mode 100644 index 0000000..3b908cd --- /dev/null +++ b/components/ThemePanel.tsx @@ -0,0 +1,73 @@ +import tailwindConfig from '@/tailwind.config.ts' +import ThemeItem from './ThemeItem' +import { create } from 'zustand' + +interface Theme { + mode: 'light' | 'dark' | 'follow-system' + light: string + dark: string +} + +interface ThemeStore { + theme: Theme + setTheme: (newTheme: string) => void +} + +export const useThemeStore = create((set) => ({ + theme: { light: 'ca', dark: 'dark', mode: 'follow-system' }, + setTheme: (newTheme: string) => {}, +})) + +function getTheme(theme: Theme) { + // 根据传入的 theme 配置返回具体的主题 + if (theme.mode === 'light') { + return theme.light + } + + if (theme.mode === 'dark') { + return theme.dark + } + + // follow-system 模式下根据系统主题返回 + if (typeof window !== 'undefined') { + const prefersDark = window.matchMedia( + '(prefers-color-scheme: dark)', + ).matches + return prefersDark ? theme.dark : theme.light + } + + // 默认返回 light 主题 + return theme.light +} + +export const ThemePanel: React.FC = (props: React.ComponentProps<'div'>) => { + const { theme, setTheme } = useThemeStore() + return ( +
+

Theme

+
+ {tailwindConfig.daisyui.themes.map((t, i) => ( + { + document + .getElementsByTagName('html')[0] + .setAttribute('data-theme', t) + window.localStorage.setItem( + 'sb-react-daisyui-preview-theme', + t, + ) + setTheme(t) + }} + /> + ))} +
+
+ ) +} diff --git a/package.json b/package.json index 472b23c..851c823 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@codemirror/legacy-modes": "^6.4.2", "@uiw/codemirror-theme-vscode": "^4.23.7", "@uiw/react-codemirror": "^4.23.7", + "clsx": "^2.1.1", "codemirror-lang-elixir": "^4.0.0", "next": "15.1.2", "react": "^19.0.0", @@ -21,6 +22,7 @@ "react-dom": "^19.0.0", "socket.io": "^4.8.1", "socket.io-client": "^4.8.1", + "tailwind-merge": "^2.6.0", "ts-node": "^10.9.2", "zustand": "^5.0.2" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2075b47..66015dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,9 @@ importers: '@uiw/react-codemirror': specifier: ^4.23.7 version: 4.23.7(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.4)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.0)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.1)(codemirror@6.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + clsx: + specifier: ^2.1.1 + version: 2.1.1 codemirror-lang-elixir: specifier: ^4.0.0 version: 4.0.0 @@ -47,6 +50,9 @@ importers: socket.io-client: specifier: ^4.8.1 version: 4.8.1 + tailwind-merge: + specifier: ^2.6.0 + version: 2.6.0 ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@20.17.10)(typescript@5.7.2) @@ -718,6 +724,10 @@ packages: client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + codemirror-lang-elixir@4.0.0: resolution: {integrity: sha512-mzFesxo/t6KOxwnkqVd34R/q7yk+sMtHh6vUKGAvjwHmpL7bERHB+vQAsmU/nqrndkwVeJEHWGw/z/ybfdiudA==} @@ -1826,6 +1836,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + tailwind-merge@2.6.0: + resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} + tailwindcss@3.4.17: resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} engines: {node: '>=14.0.0'} @@ -2705,6 +2718,8 @@ snapshots: client-only@0.0.1: {} + clsx@2.1.1: {} + codemirror-lang-elixir@4.0.0: dependencies: '@codemirror/language': 6.10.8 @@ -4061,6 +4076,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + tailwind-merge@2.6.0: {} + tailwindcss@3.4.17(ts-node@10.9.2(@types/node@20.17.10)(typescript@5.7.2)): dependencies: '@alloc/quick-lru': 5.2.0 diff --git a/run.fish b/run.fish new file mode 100644 index 0000000..8d4513d --- /dev/null +++ b/run.fish @@ -0,0 +1,10 @@ +function run_dev + pnpm run dev & + pnpm next dev & + function cleanup --on-signal INT + kill (jobs -p) + exit + end + wait +end +run_dev diff --git a/tailwind.config.ts b/tailwind.config.ts index f608c30..fd4a56f 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -6,6 +6,8 @@ export default { './pages/**/*.{js,ts,jsx,tsx,mdx}', './components/**/*.{js,ts,jsx,tsx,mdx}', './app/**/*.{js,ts,jsx,tsx,mdx}', + 'node_modules/daisyui/dist/**/*.js', + 'node_modules/react-daisyui/dist/**/*.js', ], theme: { extend: {