diff --git a/.eslintrc b/.eslintrc index 21bb2ee..b97ac9f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -42,6 +42,7 @@ "@typescript-eslint/no-floating-promises": "off", "@typescript-eslint/restrict-template-expressions": "off", "@typescript-eslint/no-misused-promises": "off", - "@typescript-eslint/consistent-type-assertions": "off" + "@typescript-eslint/consistent-type-assertions": "off", + "import/no-absolute-path": "off" } } diff --git a/package.json b/package.json index 4b06211..8b97e1f 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "dependencies": { "@perfsee/jsonr": "^1.8.4", "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-hover-card": "^1.0.7", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-popover": "^1.0.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 85c208c..18e9f9e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ dependencies: '@radix-ui/react-avatar': specifier: ^1.0.4 version: 1.0.4(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-dropdown-menu': + specifier: ^2.0.6 + version: 2.0.6(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-hover-card': specifier: ^1.0.7 version: 1.0.7(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) @@ -1310,6 +1313,30 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.2 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.35)(react@18.2.0) + '@types/react': 18.2.35 + '@types/react-dom': 18.2.14 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.35)(react@18.2.0): resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} peerDependencies: @@ -1377,6 +1404,33 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.2 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-menu': 2.0.6(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@types/react': 18.2.35 + '@types/react-dom': 18.2.14 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.35)(react@18.2.0): resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} peerDependencies: @@ -1466,6 +1520,44 @@ packages: react: 18.2.0 dev: false + /@radix-ui/react-menu@2.0.6(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.2 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@types/react': 18.2.35 + '@types/react-dom': 18.2.14 + aria-hidden: 1.2.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.35)(react@18.2.0) + dev: false + /@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==} peerDependencies: @@ -1595,6 +1687,35 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.2 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.35)(react@18.2.0) + '@types/react': 18.2.35 + '@types/react-dom': 18.2.14 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-scroll-area@1.0.5(@types/react-dom@18.2.14)(@types/react@18.2.35)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-b6PAgH4GQf9QEn8zbT2XUHpW5z8BzqEc7Kl11TwDrvuTrxlkcjTD5qa/bxgKr+nmuXKu4L/W5UZ4mlP/VG/5Gw==} peerDependencies: diff --git a/public/wxt.svg b/public/wxt.svg new file mode 100644 index 0000000..0e76320 --- /dev/null +++ b/public/wxt.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/app/background/index.ts b/src/app/background/index.ts new file mode 100644 index 0000000..b5aec2d --- /dev/null +++ b/src/app/background/index.ts @@ -0,0 +1,14 @@ +export default defineBackground({ + // Set manifest options + persistent: true, + type: 'module', + + main() { + browser.runtime.onMessage.addListener(async (message) => { + console.log('Background recieved:', message) + console.log('Background sending:', 'pong') + browser.runtime.openOptionsPage() + return 'pong' + }) + } +}) diff --git a/src/app/content/views/AppButton/index.tsx b/src/app/content/views/AppButton/index.tsx index 0110c07..05f30b1 100644 --- a/src/app/content/views/AppButton/index.tsx +++ b/src/app/content/views/AppButton/index.tsx @@ -1,4 +1,5 @@ -import { type ReactNode, type FC } from 'react' +import { type ReactNode, type FC, useState } from 'react' +import { SettingsIcon, MoonIcon, SunIcon } from 'lucide-react' import { Button } from '@/components/ui/Button' export interface AppButtonProps { @@ -6,10 +7,50 @@ export interface AppButtonProps { } const AppButton: FC = ({ children }) => { + const [open, setOpen] = useState(false) + + const handleToggle = () => { + setOpen(!open) + } + + const handleOpenOptionsPage = () => { + console.log(browser.runtime) + + browser.runtime.sendMessage('open-options-page').then((response) => { + console.log('Popup response:', response) + }) + } + return ( - +
+
+ + + +
+ +
) } diff --git a/src/app/content/views/Footer/index.tsx b/src/app/content/views/Footer/index.tsx index e9eb593..33ec7c3 100644 --- a/src/app/content/views/Footer/index.tsx +++ b/src/app/content/views/Footer/index.tsx @@ -7,6 +7,7 @@ import MessageInputDomain from '@/domain/MessageInput' import MessageListDomain from '@/domain/MessageList' import { MESSAGE_MAX_LENGTH } from '@/constants' import EmojiButton from '@/components/EmojiButton' +import { type Message } from '@/types' const Footer: FC = () => { const send = useRemeshSend() @@ -20,14 +21,17 @@ const Footer: FC = () => { send(messageInputDomain.command.InputCommand(value)) } - const message = { + const message: Omit = { username: '墨绿青苔', - avatar: 'https://avatars.githubusercontent.com/u/10251037?v=4', + userId: '10251037', + userAvatar: 'https://avatars.githubusercontent.com/u/10251037?v=4', body: messageBody.trim(), date: Date.now(), likeChecked: false, likeCount: 0, + linkUsers: [], hateChecked: false, + hateUsers: [], hateCount: 0 } diff --git a/src/app/options/App.css b/src/app/options/App.css new file mode 100644 index 0000000..f8aa57a --- /dev/null +++ b/src/app/options/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #54bc4ae0); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/src/app/options/App.tsx b/src/app/options/App.tsx new file mode 100644 index 0000000..d490413 --- /dev/null +++ b/src/app/options/App.tsx @@ -0,0 +1,31 @@ +import { useState } from 'react' +import wxtLogo from '/wxt.svg' +import reactLogo from '@/assets/react.svg' +import './App.css' + +function App() { + const [count, setCount] = useState(0) + + return ( + <> +
+ + WXT logo + + + React logo + +
+

WXT + React

+
+ +

+ Edit src/App.tsx and save to test HMR +

+
+

Click on the WXT and React logos to learn more

+ + ) +} + +export default App diff --git a/src/app/options/index.html b/src/app/options/index.html new file mode 100644 index 0000000..7e4b0ca --- /dev/null +++ b/src/app/options/index.html @@ -0,0 +1,13 @@ + + + + + + User Config + + + +
+ + + diff --git a/src/app/options/main.tsx b/src/app/options/main.tsx new file mode 100644 index 0000000..f9e120b --- /dev/null +++ b/src/app/options/main.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.tsx' +import './style.css' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + +) diff --git a/src/app/options/style.css b/src/app/options/style.css new file mode 100644 index 0000000..2c3fac6 --- /dev/null +++ b/src/app/options/style.css @@ -0,0 +1,69 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..8e0e0f1 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + diff --git a/src/components/MessageItem.tsx b/src/components/MessageItem.tsx index f9603d9..65677a6 100644 --- a/src/components/MessageItem.tsx +++ b/src/components/MessageItem.tsx @@ -4,8 +4,8 @@ import { FrownIcon, ThumbsUpIcon } from 'lucide-react' import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/Avatar' import LikeButton from '@/components/LikeButton' -import { type Message } from '@/@types' import { Markdown } from '@/components/ui/Markdown' +import { type Message } from '@/types' export interface MessageItemProps { data: Message @@ -34,7 +34,7 @@ const MessageItem: FC = ({ data, index }) => { className="box-border grid grid-cols-[auto_1fr] gap-x-2 px-4 [content-visibility:auto] first:pt-4 last:pb-4" > - + {formatData.username}
diff --git a/src/domain/MessageList.ts b/src/domain/MessageList.ts index 9117c91..3655151 100644 --- a/src/domain/MessageList.ts +++ b/src/domain/MessageList.ts @@ -3,7 +3,7 @@ import { ListModule } from 'remesh/modules/list' import { nanoid } from 'nanoid' import { from, map, tap, merge } from 'rxjs' import Storage from './externs/Storage' -import { type Message } from '@/@types' +import { type Message } from '@/types' const MessageListDomain = Remesh.domain({ name: 'MessageListDomain', diff --git a/env.d.ts b/src/types/env.d.ts similarity index 100% rename from env.d.ts rename to src/types/env.d.ts diff --git a/src/@types/index.d.ts b/src/types/index.d.ts similarity index 68% rename from src/@types/index.d.ts rename to src/types/index.d.ts index 4d12e2d..bdba828 100644 --- a/src/@types/index.d.ts +++ b/src/types/index.d.ts @@ -1,11 +1,14 @@ export interface Message { id: string + userId: string body: string username: string - avatar: string + userAvatar: string date: number + linkUsers: string[] likeChecked: boolean hateChecked: boolean likeCount: number + hateUsers: string[] hateCount: number } diff --git a/src/types/shim.d.ts b/src/types/shim.d.ts new file mode 100644 index 0000000..65c3c23 --- /dev/null +++ b/src/types/shim.d.ts @@ -0,0 +1,17 @@ +// issues: https://github.com/facebook/react/issues/17157 +// issues: https://github.com/facebook/react/pull/24730 +declare module 'react' { + interface HTMLAttributes extends AriaAttributes, DOMAttributes { + inert?: boolean | undefined | '' + } +} + +declare global { + namespace JSX { + interface IntrinsicAttributes { + inert?: boolean | undefined | '' + } + } +} + +export {} diff --git a/tsconfig.json b/tsconfig.json index 9b364da..5ed7ca9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,9 @@ "extends": "./.wxt/tsconfig.json", "compilerOptions": { "allowImportingTsExtensions": true, - "jsx": "react-jsx" + "jsx": "react-jsx", + "paths": { + "@/*": ["./src/*"] + } } }