diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f19d830..06c5fee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -311,6 +311,9 @@ importers: react-router-dom: specifier: ^6.15.0 version: 6.15.0(react-dom@18.2.0)(react@18.2.0) + rehype-external-links: + specifier: ^3.0.0 + version: 3.0.0 socket.io-client: specifier: ^4.7.2 version: 4.7.2 @@ -5013,7 +5016,6 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] requiresBuild: true dev: false optional: true @@ -5023,7 +5025,6 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] requiresBuild: true dev: false optional: true @@ -5033,7 +5034,6 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] requiresBuild: true dev: false optional: true @@ -5043,7 +5043,6 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] requiresBuild: true dev: false optional: true @@ -13642,6 +13641,12 @@ packages: vfile-location: 5.0.2 web-namespaces: 2.0.1 + /hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + dependencies: + '@types/hast': 3.0.4 + dev: false + /hast-util-parse-selector@3.1.1: resolution: {integrity: sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==} dependencies: @@ -14330,6 +14335,11 @@ packages: resolution: {integrity: sha512-QGOS8MRMnj/UiOa+aMIgfyHcvkhqNUsUxb1XzskENvbo+rEfp6TOwqd1KPuDzXC4OnGHcMSVxDGRoilqB8ViqA==} dev: false + /is-absolute-url@4.0.1: + resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: false + /is-absolute@1.0.0: resolution: {integrity: sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==} engines: {node: '>=0.10.0'} @@ -21072,6 +21082,17 @@ packages: resolution: {integrity: sha512-bEAtp/qrtKucxXSJkD4ebopFZYP0q1+3Vb2WECWv/T8yQEgKxDxJ7ztO285tAMaYZVR6mM1GgI6CCn8FROtL1w==} dev: false + /rehype-external-links@3.0.0: + resolution: {integrity: sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw==} + dependencies: + '@types/hast': 3.0.4 + '@ungap/structured-clone': 1.2.0 + hast-util-is-element: 3.0.0 + is-absolute-url: 4.0.1 + space-separated-tokens: 2.0.2 + unist-util-visit: 5.0.0 + dev: false + /rehype-raw@6.1.1: resolution: {integrity: sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ==} dependencies: diff --git a/src/client/components/MarkdownEditor/editor.tsx b/src/client/components/MarkdownEditor/editor.tsx index c61d9c6..bcdf209 100644 --- a/src/client/components/MarkdownEditor/editor.tsx +++ b/src/client/components/MarkdownEditor/editor.tsx @@ -1,8 +1,6 @@ import React from 'react'; import { Editor, EditorProps } from '@bytemd/react'; import { plugins } from './plugins'; -import 'bytemd/dist/index.css'; -import './style.less'; import { useLocale } from './useLocale'; interface MarkdownEditorProps extends EditorProps {} diff --git a/src/client/components/MarkdownEditor/index.tsx b/src/client/components/MarkdownEditor/index.tsx index dec238d..bbd471c 100644 --- a/src/client/components/MarkdownEditor/index.tsx +++ b/src/client/components/MarkdownEditor/index.tsx @@ -1,4 +1,6 @@ import loadable from '@loadable/component'; +import 'bytemd/dist/index.css'; +import './style.less'; export const MarkdownEditor = loadable(() => import('./editor').then((module) => module.MarkdownEditor) diff --git a/src/client/components/MarkdownEditor/plugins.ts b/src/client/components/MarkdownEditor/plugins.ts index 5c20381..f25be34 100644 --- a/src/client/components/MarkdownEditor/plugins.ts +++ b/src/client/components/MarkdownEditor/plugins.ts @@ -1,6 +1,13 @@ import gfm from '@bytemd/plugin-gfm'; +import { BytemdPlugin } from 'bytemd'; +import externalLinks from 'rehype-external-links'; -export const plugins = [ +export const plugins: BytemdPlugin[] = [ gfm(), - // Add more plugins here + { + rehype: (p) => + p.use(externalLinks, { + target: '_blank', + }), + }, ]; diff --git a/src/client/components/MarkdownEditor/style.less b/src/client/components/MarkdownEditor/style.less index 2349395..8942baf 100644 --- a/src/client/components/MarkdownEditor/style.less +++ b/src/client/components/MarkdownEditor/style.less @@ -7,12 +7,20 @@ z-index: 99; } +.markdown { + .markdown-body { + a { + text-decoration: underline dotted; + } + } +} + .dark { .bytemd { - @apply bg-zinc-900 border-zinc-800 text-foreground; + @apply text-foreground border-zinc-800 bg-zinc-900; .bytemd-toolbar { - @apply bg-zinc-900 border-zinc-800; + @apply border-zinc-800 bg-zinc-900; .bytemd-toolbar-icon:hover { @apply bg-muted; @@ -24,7 +32,7 @@ } .CodeMirror { - @apply bg-zinc-900 text-foreground; + @apply text-foreground bg-zinc-900; .CodeMirror-cursor { @apply border-foreground; diff --git a/src/client/components/MarkdownEditor/viewer.tsx b/src/client/components/MarkdownEditor/viewer.tsx index 7f6107d..94c635d 100644 --- a/src/client/components/MarkdownEditor/viewer.tsx +++ b/src/client/components/MarkdownEditor/viewer.tsx @@ -1,15 +1,17 @@ import React from 'react'; import { Viewer } from '@bytemd/react'; import { plugins } from './plugins'; -import 'bytemd/dist/index.css'; -import './style.less'; interface MarkdownViewerProps { value: string; } export const MarkdownViewer: React.FC = React.memo( (props) => { - return ; + return ( +
+ +
+ ); } ); MarkdownViewer.displayName = 'MarkdownViewer'; diff --git a/src/client/components/feed/FeedEventItem.tsx b/src/client/components/feed/FeedEventItem.tsx index e1a5def..def4595 100644 --- a/src/client/components/feed/FeedEventItem.tsx +++ b/src/client/components/feed/FeedEventItem.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { Badge } from '../ui/badge'; import dayjs from 'dayjs'; import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip'; +import { MarkdownViewer } from '../MarkdownEditor'; type FeedEventItemType = AppRouterOutput['feed']['events'][number]; @@ -12,7 +13,9 @@ export const FeedEventItem: React.FC<{ event: FeedEventItemType }> = React.memo(
-
{event.eventContent}
+
+ +
diff --git a/src/client/package.json b/src/client/package.json index 985e8f6..f8029a6 100644 --- a/src/client/package.json +++ b/src/client/package.json @@ -87,6 +87,7 @@ "react-resizable-panels": "^2.0.12", "react-router": "^6.15.0", "react-router-dom": "^6.15.0", + "rehype-external-links": "^3.0.0", "socket.io-client": "^4.7.2", "sonner": "^1.4.3", "str2int": "^1.1.0", @@ -99,10 +100,10 @@ "devDependencies": { "@i18next-toolkit/cli": "^1.2.2", "@tanstack/router-vite-plugin": "^1.20.5", + "@types/jsonexport": "^3.0.5", "@types/leaflet": "^1.9.8", "@types/loadable__component": "^5.13.8", "@types/lodash-es": "^4.17.12", - "@types/jsonexport": "^3.0.5", "@types/react": "^18.2.22", "@types/react-dom": "^18.2.7", "@types/react-grid-layout": "^1.3.5", diff --git a/src/server/trpc/routers/feed/integration.ts b/src/server/trpc/routers/feed/integration.ts index 5cb6eb5..a11320e 100644 --- a/src/server/trpc/routers/feed/integration.ts +++ b/src/server/trpc/routers/feed/integration.ts @@ -43,9 +43,11 @@ export const feedIntegrationRouter = router({ } if (eventType === 'push') { - const pusher = `${_.get(data, 'pusher.name')}<${_.get(data, 'pusher.email')}>`; + const pusherName = _.get(data, 'pusher.name'); + const pusherEmail = _.get(data, 'pusher.email'); const commits = _.map(_.get(data, 'commits') as any[], 'id').join(', '); const fullName = _.get(data, 'repository.full_name'); + const repoUrl = _.get(data, 'repository.html_url'); const ref = String(_.get(data, 'ref')); const senderId = String(_.get(data, 'sender.id')); const senderName = String(_.get(data, 'sender.login')); @@ -54,7 +56,7 @@ export const feedIntegrationRouter = router({ data: { channelId: channelId, eventName: eventType, - eventContent: `${pusher} push commit ${commits} to [${ref}] in ${fullName}`, + eventContent: `[${pusherName}](${pusherEmail}) push commit **${commits}** to [${ref}] in [${fullName}](${repoUrl})`, tags: [], source: 'github', senderId, @@ -69,10 +71,13 @@ export const feedIntegrationRouter = router({ serializeJSON(event) ); + console.log('serializeJSON(event)', serializeJSON(event)); + return 'ok'; } else if (eventType === 'star') { const starCount = _.get(data, 'repository.stargazers_count'); const fullName = _.get(data, 'repository.full_name'); + const repoUrl = _.get(data, 'repository.html_url'); const senderId = String(_.get(data, 'sender.id')); const senderName = String(_.get(data, 'sender.login')); const url = String(_.get(data, 'compare')); @@ -80,7 +85,7 @@ export const feedIntegrationRouter = router({ data: { channelId: channelId, eventName: eventType, - eventContent: `${senderName} star repo [${fullName}], now is ${starCount}.`, + eventContent: `${senderName} star repo [${fullName}](${repoUrl}), now is ${starCount}.`, tags: [], source: 'github', senderId, @@ -100,6 +105,7 @@ export const feedIntegrationRouter = router({ const action = _.get(data, 'action') as 'opened' | 'closed'; const starCount = _.get(data, 'repository.stargazers_count'); const fullName = _.get(data, 'repository.full_name'); + const repoUrl = _.get(data, 'repository.html_url'); const senderId = String(_.get(data, 'sender.id')); const senderName = String(_.get(data, 'sender.login')); const url = String(_.get(data, 'issue.url')); @@ -109,10 +115,10 @@ export const feedIntegrationRouter = router({ let eventContent = ''; if (action === 'opened') { eventName = 'open_issue'; - eventContent = `${senderName} open issue [${title}] in repo [${fullName}]`; + eventContent = `${senderName} open issue [${title}] in repo [${fullName}](${repoUrl})`; } else if (action === 'closed') { eventName = 'close_issue'; - eventContent = `${senderName} close issue [${title}] in repo [${fullName}]`; + eventContent = `${senderName} close issue [${title}] in repo [${fullName}](${repoUrl})`; } if (eventContent) {