From d004bd4aeb97545479a8170ac5ae5c9796cca623 Mon Sep 17 00:00:00 2001 From: TechQuery Date: Sun, 13 Jul 2025 18:04:43 +0800 Subject: [PATCH] [add] API, components & page of GitHub issue forked from https://github.com/kaiyuanshe/OSS-toolbox --- components/Git/Issue/Card.tsx | 74 ++++++++++++++ components/Git/Issue/IssueModule.tsx | 40 ++++++++ components/Navigator/MainNavigator.tsx | 1 + models/Base.ts | 46 ++++++++- models/Repository.ts | 63 ++++++++++++ models/configuration.ts | 9 +- next.config.ts | 20 ++++ package.json | 2 + pages/api/GitHub/[...slug].ts | 12 +++ pages/api/GitHub/core.ts | 35 +++++++ pages/api/GitHub/raw/[...slug].ts | 35 +++++++ pages/issue.tsx | 58 +++++++++++ pnpm-lock.yaml | 132 +++++++++++++++++++++++++ translation/en-US.ts | 4 + translation/zh-CN.ts | 4 + translation/zh-TW.ts | 4 + 16 files changed, 537 insertions(+), 2 deletions(-) create mode 100644 components/Git/Issue/Card.tsx create mode 100644 components/Git/Issue/IssueModule.tsx create mode 100644 models/Repository.ts create mode 100644 pages/api/GitHub/[...slug].ts create mode 100644 pages/api/GitHub/core.ts create mode 100644 pages/api/GitHub/raw/[...slug].ts create mode 100644 pages/issue.tsx diff --git a/components/Git/Issue/Card.tsx b/components/Git/Issue/Card.tsx new file mode 100644 index 0000000..5b37eb4 --- /dev/null +++ b/components/Git/Issue/Card.tsx @@ -0,0 +1,74 @@ +import { Icon, Nameplate, text2color } from 'idea-react'; +import { marked } from 'marked'; +import { Issue } from 'mobx-github'; +import { FC } from 'react'; +import { Badge, Card, CardProps, Stack } from 'react-bootstrap'; + +export type IssueCardProps = Issue & Omit; + +export const IssueCard: FC = ({ + bg = 'light', + text = 'dark', + id, + number, + title, + labels, + body, + html_url, + user, + comments, + created_at, + ...props +}) => ( + + + + #{number} {title} + + + {labels.map( + label => + typeof label === 'object' && ( + + {label.name} + + ), + )} + + + + + {user && } + + + + {comments} + + + + + +); diff --git a/components/Git/Issue/IssueModule.tsx b/components/Git/Issue/IssueModule.tsx new file mode 100644 index 0000000..9d71418 --- /dev/null +++ b/components/Git/Issue/IssueModule.tsx @@ -0,0 +1,40 @@ +import { text2color } from 'idea-react'; +import type { GitRepository } from 'mobx-github'; +import { FC } from 'react'; +import { Accordion, Badge, Col, Row } from 'react-bootstrap'; + +import { IssueCard } from './Card'; + +export const IssueModule: FC = ({ name, language, issues }) => ( + + + + + {language && ( + + {language} + + )} + + + {name} + + + + {issues?.length} + + + + + + + + {issues?.map(issue => ( + + + + ))} + + + +); diff --git a/components/Navigator/MainNavigator.tsx b/components/Navigator/MainNavigator.tsx index 06cfbf0..6b82d66 100644 --- a/components/Navigator/MainNavigator.tsx +++ b/components/Navigator/MainNavigator.tsx @@ -19,6 +19,7 @@ const topNavBarMenu = ({ t }: typeof i18n): MenuItem[] => [ href: '/article/open-collaborator-award', name: t('open_collaborator_award'), }, + { href: '/issue', name: 'GitHub issues' }, { href: 'https://github.com/Open-Source-Bazaar/Git-Hackathon-scaffold', name: t('hackathon'), diff --git a/models/Base.ts b/models/Base.ts index 1cffc9f..03935e1 100644 --- a/models/Base.ts +++ b/models/Base.ts @@ -1,9 +1,53 @@ import 'core-js/full/array/from-async'; import { HTTPClient } from 'koajax'; +import { githubClient } from 'mobx-github'; import { TableCellAttachment, TableCellMedia, TableCellValue } from 'mobx-lark'; +import { DataObject } from 'mobx-restful'; +import { isEmpty } from 'web-utility'; -import { LARK_API_HOST } from './configuration'; +import { + API_Host, + GithubToken, + isServer, + ProxyBaseURL, + LARK_API_HOST, +} from './configuration'; + +export const ownClient = new HTTPClient({ + baseURI: `${API_Host}/api/`, + responseType: 'json', +}); + +if (!isServer()) githubClient.baseURI = `${API_Host}/api/GitHub/`; + +githubClient.use(({ request }, next) => { + if (GithubToken) + request.headers = { + authorization: `Bearer ${GithubToken}`, + ...request.headers, + }; + return next(); +}); + +export { githubClient }; + +export const githubRawClient = new HTTPClient({ + baseURI: `${ProxyBaseURL}/raw.githubusercontent.com/`, + responseType: 'arraybuffer', +}); + +export interface GithubSearchData { + total_count: number; + incomplete_results: boolean; + items: T[]; +} + +export const makeGithubSearchCondition = (queryMap: DataObject) => + Object.entries(queryMap) + .filter(([, value]) => !isEmpty(value)) + .map(([key, value]) => `${key}:${value}`) + .join(' '); export const larkClient = new HTTPClient({ baseURI: LARK_API_HOST, diff --git a/models/Repository.ts b/models/Repository.ts new file mode 100644 index 0000000..8e7f256 --- /dev/null +++ b/models/Repository.ts @@ -0,0 +1,63 @@ +import { Repository, RepositoryModel, UserModel } from 'mobx-github'; +import { Filter, ListModel, toggle } from 'mobx-restful'; +import { buildURLData } from 'web-utility'; + +import { + githubClient, + githubRawClient, + GithubSearchData, + makeGithubSearchCondition, +} from './Base'; + +export class GitRepositoryModel extends RepositoryModel { + @toggle('downloading') + async downloadRaw( + path: string, + repository = this.currentOne.name, + ref = this.currentOne.default_branch, + ) { + const owner = this.owner || (await userStore.getSession()).login; + const identity = `${owner}/${repository}`; + + if (!ref) { + const { default_branch } = await this.getOne(identity); + + ref = default_branch; + } + const { body } = await githubRawClient.get( + `${identity}/${ref}/${path}`, + ); + + return body!; + } +} + +export const userStore = new UserModel(); +export const repositoryStore = new GitRepositoryModel('Open-Source-Bazaar'); + +export type RepositoryFilter = Filter; + +export class RepositorySearchModel extends ListModel< + Repository, + RepositoryFilter +> { + baseURI = 'search/repositories'; + client = githubClient; + + async loadPage( + page = this.pageIndex, + per_page = this.pageSize, + { full_name }: RepositoryFilter, + ) { + const name = full_name?.split('/').at(-1); + + const queryMap = { in: name ? 'name' : undefined }, + keyword = name; + const condition = makeGithubSearchCondition(queryMap); + + const { body } = await this.client.get>( + `${this.baseURI}?${buildURLData({ page, per_page, q: `${condition} ${keyword}` })}`, + ); + return { pageData: body!.items, totalCount: body!.total_count }; + } +} diff --git a/models/configuration.ts b/models/configuration.ts index 91326b3..bd4e349 100644 --- a/models/configuration.ts +++ b/models/configuration.ts @@ -1,10 +1,12 @@ +import { parseCookie } from 'mobx-i18n'; + export const isServer = () => typeof window === 'undefined'; export const Name = process.env.NEXT_PUBLIC_SITE_NAME, Summary = process.env.NEXT_PUBLIC_SITE_SUMMARY, DefaultImage = process.env.NEXT_PUBLIC_LOGO!; -export const { VERCEL_URL } = process.env; +export const { VERCEL, VERCEL_URL } = process.env; export const API_Host = isServer() ? VERCEL_URL @@ -16,6 +18,11 @@ export const CACHE_HOST = process.env.NEXT_PUBLIC_CACHE_HOST!; export const LARK_API_HOST = `${API_Host}/api/Lark/`; +export const ProxyBaseURL = 'https://bazaar.fcc-cd.dev/proxy'; + +export const GithubToken = + (globalThis.document && parseCookie().token) || process.env.GITHUB_TOKEN; + export const LarkAppMeta = { host: process.env.NEXT_PUBLIC_LARK_API_HOST, id: process.env.NEXT_PUBLIC_LARK_APP_ID!, diff --git a/next.config.ts b/next.config.ts index 72f96c0..f5131f1 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,4 +1,5 @@ import setMDX from '@next/mdx'; +import { NextConfig } from 'next'; import setPWA from 'next-pwa'; // @ts-expect-error no official types import withLess from 'next-with-less'; @@ -21,11 +22,30 @@ const withMDX = setMDX({ disable: isDev, }); +const rewrites: NextConfig['rewrites'] = async () => ({ + beforeFiles: [ + { + source: '/proxy/github.com/:path*', + destination: 'https://github.com/:path*', + }, + { + source: '/proxy/raw.githubusercontent.com/:path*', + destination: 'https://raw.githubusercontent.com/:path*', + }, + { + source: '/proxy/geo.datav.aliyun.com/:path*', + destination: 'https://geo.datav.aliyun.com/:path*', + }, + ], + afterFiles: [], +}); + export default withPWA( withLess( withMDX({ pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'], output: CI ? 'standalone' : undefined, + rewrites, }), ), ); diff --git a/package.json b/package.json index 559755b..0427666 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,12 @@ "marked": "^16.0.0", "mime": "^4.0.7", "mobx": "^6.13.7", + "mobx-github": "^0.3.11", "mobx-i18n": "^0.7.1", "mobx-lark": "^2.2.0", "mobx-react": "^9.2.0", "mobx-restful": "^2.1.0", + "mobx-restful-table": "^2.5.2", "next": "^15.3.5", "next-pwa": "^5.6.0", "next-ssr-middleware": "^1.0.1", diff --git a/pages/api/GitHub/[...slug].ts b/pages/api/GitHub/[...slug].ts new file mode 100644 index 0000000..df28ca6 --- /dev/null +++ b/pages/api/GitHub/[...slug].ts @@ -0,0 +1,12 @@ +import { createKoaRouter, withKoaRouter } from 'next-ssr-middleware'; + +import { safeAPI } from '../core'; +import { proxyGitHubAll } from './core'; + +export const config = { api: { bodyParser: false } }; + +const router = createKoaRouter(import.meta.url); + +router.get('/(.*)', safeAPI, proxyGitHubAll); + +export default withKoaRouter(router); diff --git a/pages/api/GitHub/core.ts b/pages/api/GitHub/core.ts new file mode 100644 index 0000000..a9d5c2e --- /dev/null +++ b/pages/api/GitHub/core.ts @@ -0,0 +1,35 @@ +import { Context, Middleware } from 'koa'; +import { githubOAuth2 } from 'next-ssr-middleware'; + +import { githubClient } from '../../../models/Base'; +import { ProxyBaseURL, VERCEL } from '../../../models/configuration'; + +export const proxyGithub = async ({ + method, + url, + headers: { host, ...headers }, + request, +}: Context) => { + const path = url!.slice(`/api/GitHub/`.length), + body = Reflect.get(request, 'body'); + + // @ts-expect-error KoAJAX type compatibility + return githubClient.request({ method, path, headers, body }); +}; + +export const proxyGitHubAll: Middleware = async context => { + const { status, body } = await proxyGithub(context); + + context.status = status; + context.body = body; +}; + +const client_id = process.env.GITHUB_OAUTH_CLIENT_ID!, + client_secret = process.env.GITHUB_OAUTH_CLIENT_SECRET!; + +export const githubOAuth = githubOAuth2({ + rootBaseURL: VERCEL ? undefined : `${ProxyBaseURL}/github.com/`, + client_id, + client_secret, + scopes: ['user', 'repo'], +}); diff --git a/pages/api/GitHub/raw/[...slug].ts b/pages/api/GitHub/raw/[...slug].ts new file mode 100644 index 0000000..c14045a --- /dev/null +++ b/pages/api/GitHub/raw/[...slug].ts @@ -0,0 +1,35 @@ +import { fileTypeFromBuffer } from 'file-type'; +import { Context } from 'koa'; +import { githubClient } from 'mobx-github'; +import { createKoaRouter, withKoaRouter } from 'next-ssr-middleware'; + +import { safeAPI } from '../../core'; + +export const config = { api: { bodyParser: false } }; + +const router = createKoaRouter(import.meta.url); + +router.all('/(.*)', safeAPI, async (context: Context) => { + const { method, url, headers, body } = context; + + delete headers.host; + + const path = `https://raw.githubusercontent.com/${url!.slice(`/api/GitHub/raw/`.length)}`; + + const { status, body: data } = await githubClient.request({ + // @ts-expect-error KoAJAX type compatibility + method, + path, + // @ts-expect-error KoAJAX type compatibility + headers, + body: body || undefined, + responseType: 'arraybuffer', + }); + const { mime } = (await fileTypeFromBuffer(data!)) || {}; + + context.status = status; + context.set('Content-Type', mime || 'application/octet-stream'); + context.body = data; +}); + +export default withKoaRouter(router); diff --git a/pages/issue.tsx b/pages/issue.tsx new file mode 100644 index 0000000..774902d --- /dev/null +++ b/pages/issue.tsx @@ -0,0 +1,58 @@ +import { Loading } from 'idea-react'; +import { GitRepository, RepositoryModel } from 'mobx-github'; +import { observer } from 'mobx-react'; +import { ScrollList } from 'mobx-restful-table'; +import { cache, compose, errorLogger } from 'next-ssr-middleware'; +import { FC, useContext } from 'react'; +import { Accordion, Breadcrumb, Container } from 'react-bootstrap'; + +import { IssueModule } from '../components/Git/Issue/IssueModule'; +import { PageHead } from '../components/Layout/PageHead'; +import { repositoryStore } from '../models/Repository'; +import { I18nContext } from '../models/Translation'; + +export const getServerSideProps = compose(cache(), errorLogger, async () => { + const list = await new RepositoryModel('Open-Source-Bazaar').getList({ + relation: ['issues'], + }); + + return { props: JSON.parse(JSON.stringify({ list })) }; +}); + +const IssuesPage: FC<{ list: GitRepository[] }> = observer(({ list }) => { + const i18n = useContext(I18nContext); + const { t } = i18n; + + return ( + + + + {t('open_source_bazaar')} + GitHub issues + +

GitHub issues

+ + {repositoryStore.downloading > 0 && } + + ( + + {allItems.map( + repository => + !repository.archived && + repository.issues?.[0] && ( + + ), + )} + + )} + /> +
+ ); +}); + +export default IssuesPage; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ebb139d..dadd2bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: mobx: specifier: ^6.13.7 version: 6.13.7 + mobx-github: + specifier: ^0.3.11 + version: 0.3.11(core-js@3.44.0)(typescript@5.8.3) mobx-i18n: specifier: ^0.7.1 version: 0.7.1(mobx@6.13.7)(typescript@5.8.3) @@ -59,6 +62,9 @@ importers: mobx-restful: specifier: ^2.1.0 version: 2.1.0(core-js@3.44.0)(mobx@6.13.7)(typescript@5.8.3) + mobx-restful-table: + specifier: ^2.5.2 + version: 2.5.2(@types/react@19.1.8)(core-js@3.44.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3) next: specifier: ^15.3.5 version: 15.3.5(@babel/core@7.28.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.2) @@ -1222,6 +1228,9 @@ packages: '@types/react': '>=16' react: '>=16' + '@mixmark-io/domino@2.2.0': + resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==} + '@napi-rs/wasm-runtime@0.2.11': resolution: {integrity: sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==} @@ -1306,6 +1315,9 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} + '@octokit/openapi-types@25.1.0': + resolution: {integrity: sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==} + '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} engines: {node: '>= 10.0.0'} @@ -1571,6 +1583,9 @@ packages: '@types/koa__router@12.0.4': resolution: {integrity: sha512-Y7YBbSmfXZpa/m5UGGzb7XadJIRBRnwNY9cdAojZGp65Cpe5MAP3mOZE7e3bImt8dfKS4UFcR16SLH8L/z7PBw==} + '@types/lodash@4.17.20': + resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -1627,6 +1642,9 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/turndown@5.0.5': + resolution: {integrity: sha512-TL2IgGgc7B5j78rIccBtlYAnkuv8nUQqhQc+DSYV5j9Be9XOcm/SKOVRuA47xAVI3680Tk9B1d8flK2GWT2+4w==} + '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -1971,6 +1989,9 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browser-fs-access@0.37.0: + resolution: {integrity: sha512-MKpvZrKtv6pBJ2ACd+VwfS9XauBKTMVZg2UBibypuK1gfiXM7euZjbdKmvRsyxeQRhfzNVQrzCSVGXs19/LP8Q==} + browserslist@4.25.1: resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -2309,6 +2330,9 @@ packages: editorjs-html@4.0.5: resolution: {integrity: sha512-ImQYxB3fNCJcd+nJ+Vbne/6PxidO1cYByNpu9nBDStVabfjVrMW65BuR+IEZfOii8VKYH+CW/lYDb2GDlzZtDg==} + edkit@1.2.7: + resolution: {integrity: sha512-dCOBN9MMbCaCdSqhnZTSHPe7lu53TQttttjVBxLE/TehsQasuxmqW3ckimVODFaJVci1A6w429j9bebpiU3zKg==} + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -3289,6 +3313,11 @@ packages: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} + marked@15.0.12: + resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} + engines: {node: '>= 18'} + hasBin: true + marked@16.0.0: resolution: {integrity: sha512-MUKMXDjsD/eptB7GPzxo4xcnLS6oo7/RHimUMHEDRhUooPwmN9BEpMl7AEOJv3bmso169wHI2wUF9VQgL7zfmA==} engines: {node: '>= 20'} @@ -3464,6 +3493,9 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + mobx-github@0.3.11: + resolution: {integrity: sha512-xbad5SNMmVJBKoWTnkUxzObSWQI/olk3Iy6ai2LWEbou2hfpQVre0/FaejQs593cNyheQl1RULdKctOcB4EbyQ==} + mobx-i18n@0.7.1: resolution: {integrity: sha512-J8iTlxkj19Rh75gst5IvNl8LnlBQVIRV7FAt8yaLBRhSSe4jaZxlDMwXiv0wuBZ6SUD71KYw1w4YTMAorL3O5w==} peerDependencies: @@ -3504,6 +3536,11 @@ packages: react-native: optional: true + mobx-restful-table@2.5.2: + resolution: {integrity: sha512-+LubMlc+Mm1WGJbASaIWiJndSTRSlW8DIrGwqdKKjYSQcj3/ULloQELrUoCVuid3Q4hCYdjmioarwTqTyFvBsQ==} + peerDependencies: + react: '>=16.8' + mobx-restful@2.1.0: resolution: {integrity: sha512-5fuTDldCPwU+Ji/ea5UGza8Nd1FbM4qEI5w+e/SESTrimchcAx8nvO4zWuNB/OEm3dsVqo8eMASdhnaJ5FRS8g==} peerDependencies: @@ -3819,6 +3856,12 @@ packages: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} + react-bootstrap-editor@2.1.1: + resolution: {integrity: sha512-vnAy1MSn4mAZp418cz3R7IiUWXGGwNfmWQYlPUa2MWGm6hTTxJVeKTp2jhrH3n8sbbtg2MN4/co44OP+sZn1pQ==} + peerDependencies: + react: '>=16' + react-dom: '>=16' + react-bootstrap@2.10.10: resolution: {integrity: sha512-gMckKUqn8aK/vCnfwoBpBVFUGT9SVQxwsYrp9yDHt0arXMamxALerliKBxr1TPbntirK/HGrUAHYbAeQTa9GHQ==} peerDependencies: @@ -4321,6 +4364,12 @@ packages: resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} engines: {node: '>=0.6.x'} + turndown-plugin-gfm@1.0.2: + resolution: {integrity: sha512-vwz9tfvF7XN/jE0dGoBei3FXWuvll78ohzCZQuOb+ZjWrs3a0XhQVomJEb2Qh4VHTPNRO4GPZh0V7VRbiWwkRg==} + + turndown@7.2.0: + resolution: {integrity: sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -5825,6 +5874,8 @@ snapshots: '@types/react': 19.1.8 react: 19.1.0 + '@mixmark-io/domino@2.2.0': {} + '@napi-rs/wasm-runtime@0.2.11': dependencies: '@emnapi/core': 1.4.4 @@ -5883,6 +5934,8 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@octokit/openapi-types@25.1.0': {} + '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -6168,6 +6221,8 @@ snapshots: dependencies: '@types/koa': 2.15.0 + '@types/lodash@4.17.20': {} + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 @@ -6240,6 +6295,8 @@ snapshots: '@types/trusted-types@2.0.7': {} + '@types/turndown@5.0.5': {} + '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} @@ -6601,6 +6658,8 @@ snapshots: dependencies: fill-range: 7.1.1 + browser-fs-access@0.37.0: {} + browserslist@4.25.1: dependencies: caniuse-lite: 1.0.30001727 @@ -6957,6 +7016,19 @@ snapshots: editorjs-html@4.0.5: {} + edkit@1.2.7(typescript@5.8.3): + dependencies: + '@swc/helpers': 0.5.17 + '@types/turndown': 5.0.5 + browser-fs-access: 0.37.0 + marked: 15.0.12 + regenerator-runtime: 0.14.1 + turndown: 7.2.0 + turndown-plugin-gfm: 1.0.2 + web-utility: 4.4.3(typescript@5.8.3) + transitivePeerDependencies: + - typescript + ee-first@1.1.1: {} ejs@3.1.10: @@ -8179,6 +8251,8 @@ snapshots: markdown-extensions@2.0.0: {} + marked@15.0.12: {} + marked@16.0.0: {} math-intrinsics@1.1.0: {} @@ -8530,6 +8604,21 @@ snapshots: minimist@1.2.8: {} + mobx-github@0.3.11(core-js@3.44.0)(typescript@5.8.3): + dependencies: + '@octokit/openapi-types': 25.1.0 + '@swc/helpers': 0.5.17 + '@types/lodash': 4.17.20 + koajax: 3.1.2(core-js@3.44.0)(typescript@5.8.3) + lodash: 4.17.21 + mobx: 6.13.7 + mobx-restful: 2.1.0(core-js@3.44.0)(mobx@6.13.7)(typescript@5.8.3) + web-utility: 4.4.3(typescript@5.8.3) + transitivePeerDependencies: + - core-js + - jsdom + - typescript + mobx-i18n@0.7.1(mobx@6.13.7)(typescript@5.8.3): dependencies: '@swc/helpers': 0.5.17 @@ -8579,6 +8668,29 @@ snapshots: optionalDependencies: react-dom: 19.1.0(react@19.1.0) + mobx-restful-table@2.5.2(@types/react@19.1.8)(core-js@3.44.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3): + dependencies: + '@swc/helpers': 0.5.17 + classnames: 2.5.1 + lodash: 4.17.21 + mobx: 6.13.7 + mobx-i18n: 0.7.1(mobx@6.13.7)(typescript@5.8.3) + mobx-react: 9.2.0(mobx@6.13.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + mobx-react-helper: 0.4.1(mobx@6.13.7)(react@19.1.0)(typescript@5.8.3) + mobx-restful: 2.1.0(core-js@3.44.0)(mobx@6.13.7)(typescript@5.8.3) + react: 19.1.0 + react-bootstrap: 2.10.10(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-bootstrap-editor: 2.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3) + regenerator-runtime: 0.14.1 + web-utility: 4.4.3(typescript@5.8.3) + transitivePeerDependencies: + - '@types/react' + - core-js + - jsdom + - react-dom + - react-native + - typescript + mobx-restful@2.1.0(core-js@3.44.0)(mobx@6.13.7)(typescript@5.8.3): dependencies: '@swc/helpers': 0.5.17 @@ -8904,6 +9016,20 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 + react-bootstrap-editor@2.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3): + dependencies: + '@swc/helpers': 0.5.17 + edkit: 1.2.7(typescript@5.8.3) + mobx: 6.13.7 + mobx-react: 9.2.0(mobx@6.13.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + mobx-react-helper: 0.4.1(mobx@6.13.7)(react@19.1.0)(typescript@5.8.3) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + web-utility: 4.4.3(typescript@5.8.3) + transitivePeerDependencies: + - react-native + - typescript + react-bootstrap@2.10.10(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@babel/runtime': 7.27.6 @@ -9516,6 +9642,12 @@ snapshots: tsscmp@1.0.6: {} + turndown-plugin-gfm@1.0.2: {} + + turndown@7.2.0: + dependencies: + '@mixmark-io/domino': 2.2.0 + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 diff --git a/translation/en-US.ts b/translation/en-US.ts index abbb596..5a52002 100644 --- a/translation/en-US.ts +++ b/translation/en-US.ts @@ -11,4 +11,8 @@ export default { // Search keywords: 'Keywords', search_results: 'Search Results', + + // Scroll List + load_more: 'Load more...', + no_more: 'No more', }; diff --git a/translation/zh-CN.ts b/translation/zh-CN.ts index 7baf329..55bef1d 100644 --- a/translation/zh-CN.ts +++ b/translation/zh-CN.ts @@ -11,4 +11,8 @@ export default { // Search keywords: '关键词', search_results: '搜索结果', + + // Scroll List + load_more: '加载更多……', + no_more: '没有更多', }; diff --git a/translation/zh-TW.ts b/translation/zh-TW.ts index 024e7fb..246a9ca 100644 --- a/translation/zh-TW.ts +++ b/translation/zh-TW.ts @@ -11,4 +11,8 @@ export default { // Search keywords: '關鍵詞', search_results: '搜尋結果', + + // Scroll List + load_more: '加載更多……', + no_more: '沒有更多', };