├── .bunfig.toml ├── .changelogrc.js ├── .commitlintrc.js ├── .dumirc.ts ├── .editorconfig ├── .env.example ├── .eslintignore ├── .eslintrc.js ├── .fatherrc.ts ├── .github ├── ISSUE_TEMPLATE │ ├── 1_bug_report.yml │ ├── 2_feature_request.yml │ ├── 3_question.yml │ └── other.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── auto-merge.yml │ ├── issue-check-inactive.yml │ ├── issue-close-require.yml │ ├── issue-remove-inactive.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .i18nrc.js ├── .npmrc ├── .prettierignore ├── .prettierrc.js ├── .releaserc.js ├── .remarkrc.js ├── .stylelintrc.js ├── CHANGELOG.md ├── LICENSE ├── README.md ├── clean-package.config.js ├── docs ├── changelog.md ├── index.md └── index.tsx ├── package.json ├── public └── robots.txt ├── renovate.json ├── scripts └── syncSimpleIconList.mjs ├── src ├── Readme │ └── share.ts ├── ReadmeContributing │ ├── index.md │ └── index.tsx ├── ReadmeCredits │ ├── index.md │ └── index.tsx ├── ReadmeDevelopment │ ├── index.md │ └── index.tsx ├── ReadmeFeatures │ ├── index.md │ └── index.tsx ├── ReadmeHero │ ├── index.md │ └── index.tsx ├── ReadmeInstallation │ ├── index.md │ └── index.tsx ├── ReadmeLicense │ ├── index.md │ └── index.tsx ├── ShieldShare │ ├── index.md │ └── index.tsx ├── ShieldsBilibili │ ├── index.md │ └── index.tsx ├── ShieldsCustom │ ├── Double.tsx │ ├── Single.tsx │ ├── Website.tsx │ ├── index.md │ ├── index.tsx │ └── share.ts ├── ShieldsDiscord │ ├── index.md │ └── index.tsx ├── ShieldsDocker │ ├── index.md │ ├── index.tsx │ └── share.ts ├── ShieldsGithub │ ├── Action.tsx │ ├── Codespace.tsx │ ├── Contributors.tsx │ ├── Release.tsx │ ├── Social.tsx │ ├── StarHistory.tsx │ ├── index.md │ ├── index.tsx │ └── share.ts ├── ShieldsNpm │ ├── index.md │ ├── index.tsx │ └── share.ts ├── ShieldsSocial │ ├── index.md │ └── index.tsx ├── ShieldsVercel │ ├── Deploy.tsx │ ├── Website.tsx │ ├── index.md │ ├── index.tsx │ └── share.ts ├── ShieldsWebsite │ ├── index.md │ └── index.tsx ├── Sponsor │ ├── Avatar.tsx │ ├── const.ts │ ├── demos │ │ ├── data.ts │ │ └── index.tsx │ ├── index.md │ ├── index.tsx │ ├── style.ts │ └── utils.ts ├── components │ ├── Highlight │ │ └── index.tsx │ ├── Label │ │ └── index.tsx │ ├── Markdown │ │ ├── HighlightStyle.tsx │ │ ├── index.tsx │ │ └── style.ts │ ├── MarkdownEditor │ │ ├── index.tsx │ │ └── style.ts │ ├── MarkdownPreivew │ │ ├── index.tsx │ │ └── style.ts │ ├── MarkdownStorybook │ │ ├── index.tsx │ │ └── style.ts │ └── Title │ │ └── index.tsx ├── const │ ├── dockerShieldControls.ts │ ├── githubShieldControls.ts │ ├── icons.ts │ ├── npmShieldControls.ts │ ├── sample.ts │ ├── shareShieldControls.ts │ ├── shieldBaseControls.ts │ ├── socialShieldControls.ts │ └── url.ts ├── index.ts ├── services │ ├── genCustomShield.ts │ ├── genDockerShield.ts │ ├── genGithubShield.ts │ ├── genMarkdownContributing.ts │ ├── genMarkdownCredits.ts │ ├── genMarkdownDevelopment.ts │ ├── genMarkdownFeatures.ts │ ├── genMarkdownHero.ts │ ├── genMarkdownInstallation.ts │ ├── genMarkdownLicense.ts │ ├── genNpmShield.ts │ ├── genShareShield.ts │ ├── genSocialShield.ts │ ├── genSponsor.tsx │ ├── genVercelShield.ts │ └── sponsorkit │ │ ├── github │ │ ├── index.ts │ │ └── makeQuery.ts │ │ ├── index.ts │ │ ├── opencollective │ │ ├── index.ts │ │ └── makeQuery.ts │ │ └── types.ts ├── store │ ├── action.ts │ ├── index.ts │ ├── initialState.ts │ ├── selectors.ts │ └── store.ts ├── types │ ├── i18next.d.ts │ └── shields.ts └── utils │ ├── addBackTopTop.ts │ ├── formatCustomLabel.ts │ ├── genPickList.ts │ ├── genShield.ts │ ├── genSiteHeadTitle.ts │ ├── genThemeModeImg.ts │ └── remarkFormat.ts ├── tests └── .gitkeep ├── tsconfig.json ├── vercel.json └── vitest.config.ts /.bunfig.toml: -------------------------------------------------------------------------------- 1 | [install.lockfile] 2 | 3 | save = false 4 | -------------------------------------------------------------------------------- /.changelogrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@lobehub/lint').changelog; 2 | -------------------------------------------------------------------------------- /.commitlintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@lobehub/lint').commitlint; 2 | -------------------------------------------------------------------------------- /.dumirc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'dumi'; 2 | import { SiteThemeConfig } from 'dumi-theme-lobehub'; 3 | import { INavItem } from 'dumi/dist/client/theme-api/types'; 4 | 5 | import { description, homepage } from './package.json'; 6 | 7 | const isProduction = process.env.NODE_ENV === 'production'; 8 | 9 | const isWin = process.platform === 'win32'; 10 | 11 | const nav: INavItem[] = [ 12 | { link: '/components/readme-hero', title: 'Generator' }, 13 | { link: 'https://simpleicons.org/', mode: 'override', title: 'Icons' }, 14 | { link: '/changelog', title: 'Changelog' }, 15 | ]; 16 | 17 | const themeConfig: SiteThemeConfig = { 18 | actions: [ 19 | { 20 | icon: 'Github', 21 | link: homepage, 22 | openExternal: true, 23 | text: 'Github', 24 | }, 25 | { 26 | link: '/components/readme-hero', 27 | text: 'Get Started', 28 | type: 'primary', 29 | }, 30 | ], 31 | analytics: { 32 | plausible: { 33 | domain: 'readme-wizard.lobehub.com', 34 | scriptBaseUrl: 'https://plausible.lobehub-inc.cn', 35 | }, 36 | }, 37 | apiHeader: { 38 | docUrl: `{github}/tree/master/src/{atomId}/index.md`, 39 | match: ['/components'], 40 | sourceUrl: `{github}/tree/master/src/{atomId}/index.tsx`, 41 | type: 'doc', 42 | }, 43 | description, 44 | giscus: { 45 | category: 'Ideas', 46 | categoryId: 'DIC_kwDOJloKoM4CXsCu', 47 | repo: 'lobehub/lobe-readme-wizard', 48 | repoId: 'R_kgDOKTF8TQ', 49 | }, 50 | metadata: { 51 | openGraph: { 52 | image: 53 | 'https://repository-images.githubusercontent.com/691108941/a66e25b3-1481-429c-b565-419bfb859ecb', 54 | }, 55 | }, 56 | name: 'ReadmeWizard', 57 | nav, 58 | prefersColor: { 59 | default: 'dark', 60 | switch: false, 61 | }, 62 | socialLinks: { 63 | discord: 'https://discord.gg/AYFPHvv2jT', 64 | github: homepage, 65 | }, 66 | title: 'ReadmeWizard - LobeHub', 67 | }; 68 | 69 | export default defineConfig({ 70 | base: '/', 71 | define: { 72 | 'process.env': process.env, 73 | }, 74 | exportStatic: {}, 75 | extraBabelPlugins: ['antd-style'], 76 | favicons: ['https://lobehub.com/favicon.ico'], 77 | jsMinifier: 'swc', 78 | locales: [{ id: 'en-US', name: 'English' }], 79 | mfsu: isWin ? undefined : {}, 80 | npmClient: 'pnpm', 81 | publicPath: '/', 82 | sitemap: { 83 | hostname: 'https://readme-wizard.lobehub.com', 84 | }, 85 | ssr: isProduction ? {} : false, 86 | styles: [ 87 | `html, body { background: transparent; } 88 | 89 | @media (prefers-color-scheme: dark) { 90 | html, body { background: #000; } 91 | }`, 92 | ], 93 | themeConfig, 94 | title: 'ReadmeWizard', 95 | }); 96 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | SPONSORKIT_GITHUB_TOKEN=xxxxxx 2 | SPONSORKIT_GITHUB_LOGIN=lobehub 3 | SPONSORKIT_GITHUB_TYPE=organization 4 | 5 | SPONSORKIT_OPENCOLLECTIVE_ID=lobehub 6 | SPONSORKIT_OPENCOLLECTIVE_KEY=xxxxxx 7 | SPONSORKIT_OPENCOLLECTIVE_TYPE=collective 8 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Eslintignore for LobeHub 2 | ################################################################ 3 | 4 | # dependencies 5 | node_modules 6 | 7 | # ci 8 | coverage 9 | .coverage 10 | 11 | # test 12 | jest* 13 | _test_ 14 | __test__ 15 | 16 | # umi 17 | .umi 18 | .umi-production 19 | .umi-test 20 | .dumi/tmp* 21 | !.dumirc.ts 22 | 23 | # production 24 | dist 25 | es 26 | logs 27 | 28 | # misc 29 | # add other ignore file below 30 | **/**/makeQuery.ts 31 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | const config = require('@lobehub/lint').eslint; 2 | 3 | config.rules['unicorn/no-negated-condition'] = 0; 4 | config.rules['unicorn/prefer-type-error'] = 0; 5 | config.rules['unicorn/prefer-logical-operator-over-ternary'] = 0; 6 | config.rules['unicorn/no-null'] = 0; 7 | config.rules['unicorn/no-typeof-undefined'] = 0; 8 | config.rules['unicorn/explicit-length-check'] = 0; 9 | config.rules['unicorn/prefer-code-point'] = 0; 10 | config.rules['no-extra-boolean-cast'] = 0; 11 | config.rules['unicorn/no-useless-undefined'] = 0; 12 | config.rules['react/no-unknown-property'] = 0; 13 | config.rules['unicorn/prefer-ternary'] = 0; 14 | 15 | module.exports = config; 16 | -------------------------------------------------------------------------------- /.fatherrc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'father'; 2 | 3 | export default defineConfig({ 4 | esm: { output: 'es' }, 5 | }); 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1_bug_report.yml: -------------------------------------------------------------------------------- 1 | name: '🐛 反馈缺陷 Bug Report' 2 | description: '反馈一个问题缺陷 | Report an bug' 3 | title: '[Bug] ' 4 | labels: '🐛 Bug' 5 | body: 6 | - type: dropdown 7 | attributes: 8 | label: '💻 系统环境 | Operating System' 9 | options: 10 | - Windows 11 | - macOS 12 | - Ubuntu 13 | - Other Linux 14 | - Other 15 | validations: 16 | required: true 17 | - type: dropdown 18 | attributes: 19 | label: '🌐 浏览器 | Browser' 20 | options: 21 | - Chrome 22 | - Edge 23 | - Safari 24 | - Firefox 25 | - Other 26 | validations: 27 | required: true 28 | - type: textarea 29 | attributes: 30 | label: '🐛 问题描述 | Bug Description' 31 | description: A clear and concise description of the bug. 32 | validations: 33 | required: true 34 | - type: textarea 35 | attributes: 36 | label: '🚦 期望结果 | Expected Behavior' 37 | description: A clear and concise description of what you expected to happen. 38 | - type: textarea 39 | attributes: 40 | label: '📷 复现步骤 | Recurrence Steps' 41 | description: A clear and concise description of how to recurrence. 42 | - type: textarea 43 | attributes: 44 | label: '📝 补充信息 | Additional Information' 45 | description: If your problem needs further explanation, or if the issue you're seeing cannot be reproduced in a gist, please add more information here. 46 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2_feature_request.yml: -------------------------------------------------------------------------------- 1 | name: '🌠 功能需求 Feature Request' 2 | description: '需求或建议 | Suggest an idea' 3 | title: '[Request] ' 4 | labels: '🌠 Feature Request' 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: '🥰 需求描述 | Feature Description' 9 | description: Please add a clear and concise description of the problem you are seeking to solve with this feature request. 10 | validations: 11 | required: true 12 | - type: textarea 13 | attributes: 14 | label: '🧐 解决方案 | Proposed Solution' 15 | description: Describe the solution you'd like in a clear and concise manner. 16 | validations: 17 | required: true 18 | - type: textarea 19 | attributes: 20 | label: '📝 补充信息 | Additional Information' 21 | description: Add any other context about the problem here. 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3_question.yml: -------------------------------------------------------------------------------- 1 | name: '😇 疑问或帮助 Help Wanted' 2 | description: '疑问或需要帮助 | Need help' 3 | title: '[Question] ' 4 | labels: '😇 Help Wanted' 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: '🧐 问题描述 | Proposed Solution' 9 | description: A clear and concise description of the proplem. 10 | validations: 11 | required: true 12 | - type: textarea 13 | attributes: 14 | label: '📝 补充信息 | Additional Information' 15 | description: Add any other context about the problem here. 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: '📝 其他 Other' 3 | about: '其他问题 | Other issues' 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### 💻 变更类型 | Change Type 2 | 3 | 4 | 5 | - [ ] ✨ feat 6 | - [ ] 🐛 fix 7 | - [ ] 💄 style 8 | - [ ] 🔨 chore 9 | - [ ] 📝 docs 10 | 11 | #### 🔀 变更说明 | Description of Change 12 | 13 | 14 | 15 | #### 📝 补充信息 | Additional Information 16 | 17 | 18 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot Auto Merge 2 | on: 3 | pull_request_target: 4 | types: [labeled, edited] 5 | 6 | jobs: 7 | merge: 8 | if: contains(github.event.pull_request.labels.*.name, 'dependencies') 9 | name: Dependabot Auto Merge 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Install bun 15 | uses: oven-sh/setup-bun@v2 16 | 17 | - name: Install deps 18 | run: bun i 19 | 20 | - name: Merge 21 | uses: ahmadnassri/action-dependabot-auto-merge@v2 22 | with: 23 | command: merge 24 | target: minor 25 | github-token: ${{ secrets.GH_TOKEN }} 26 | -------------------------------------------------------------------------------- /.github/workflows/issue-check-inactive.yml: -------------------------------------------------------------------------------- 1 | name: Issue Check Inactive 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 */15 * *' 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | issue-check-inactive: 12 | permissions: 13 | issues: write # for actions-cool/issues-helper to update issues 14 | pull-requests: write # for actions-cool/issues-helper to update PRs 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: check-inactive 18 | uses: actions-cool/issues-helper@v3 19 | with: 20 | actions: 'check-inactive' 21 | inactive-label: 'Inactive' 22 | inactive-day: 30 23 | -------------------------------------------------------------------------------- /.github/workflows/issue-close-require.yml: -------------------------------------------------------------------------------- 1 | name: Issue Close Require 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | issue-close-require: 12 | permissions: 13 | issues: write # for actions-cool/issues-helper to update issues 14 | pull-requests: write # for actions-cool/issues-helper to update PRs 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: need reproduce 18 | uses: actions-cool/issues-helper@v3 19 | with: 20 | actions: 'close-issues' 21 | labels: '✅ Fixed' 22 | inactive-day: 3 23 | body: | 24 | Since the issue was labeled with `✅ Fixed`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply. 25 | 26 | 由于该 issue 被标记为已修复,同时 3 天未收到回应。现关闭 issue,若有任何问题,可评论回复。 27 | - name: need reproduce 28 | uses: actions-cool/issues-helper@v3 29 | with: 30 | actions: 'close-issues' 31 | labels: '🤔 Need Reproduce' 32 | inactive-day: 3 33 | body: | 34 | Since the issue was labeled with `🤔 Need Reproduce`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply. 35 | 36 | 由于该 issue 被标记为需要更多信息,却 3 天未收到回应。现关闭 issue,若有任何问题,可评论回复。 37 | - name: need reproduce 38 | uses: actions-cool/issues-helper@v3 39 | with: 40 | actions: 'close-issues' 41 | labels: "🙅🏻‍♀️ WON'T DO" 42 | inactive-day: 3 43 | body: | 44 | Since the issue was labeled with `🙅🏻‍♀️ WON'T DO`, and no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply. 45 | 46 | 由于该 issue 被标记为暂不处理,同时 3 天未收到回应。现关闭 issue,若有任何问题,可评论回复。 47 | -------------------------------------------------------------------------------- /.github/workflows/issue-remove-inactive.yml: -------------------------------------------------------------------------------- 1 | name: Issue Remove Inactive 2 | 3 | on: 4 | issues: 5 | types: [edited] 6 | issue_comment: 7 | types: [created, edited] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | issue-remove-inactive: 14 | permissions: 15 | issues: write # for actions-cool/issues-helper to update issues 16 | pull-requests: write # for actions-cool/issues-helper to update PRs 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: remove inactive 20 | if: github.event.issue.state == 'open' && github.actor == github.event.issue.user.login 21 | uses: actions-cool/issues-helper@v3 22 | with: 23 | actions: 'remove-labels' 24 | issue-number: ${{ github.event.issue.number }} 25 | labels: 'Inactive' 26 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | release: 9 | name: Release 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Install bun 15 | uses: oven-sh/setup-bun@v2 16 | 17 | - name: Install deps 18 | run: bun i 19 | 20 | - name: CI 21 | run: bun run ci 22 | 23 | - name: Test 24 | run: bun run test 25 | 26 | - name: Build 27 | run: bun run build 28 | 29 | - name: Release 30 | run: bun run release 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 33 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 34 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test CI 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - '!main' 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Install bun 15 | uses: oven-sh/setup-bun@v2 16 | 17 | - name: Install deps 18 | run: bun i 19 | 20 | - name: CI 21 | run: bun run ci 22 | 23 | - name: Test and coverage 24 | run: bun run test:coverage 25 | 26 | - name: Upload coverage to Codecov 27 | uses: codecov/codecov-action@v4 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gitignore for LobeHub 2 | ################################################################ 3 | 4 | # general 5 | .DS_Store 6 | .idea 7 | .vscode 8 | .history 9 | .temp 10 | .env.local 11 | venv 12 | temp 13 | tmp 14 | 15 | # dependencies 16 | node_modules 17 | *.log 18 | *.lock 19 | package-lock.json 20 | 21 | # ci 22 | coverage 23 | .coverage 24 | .eslintcache 25 | .stylelintcache 26 | 27 | # production 28 | dist 29 | es 30 | logs 31 | test-output 32 | 33 | # umi 34 | .umi 35 | .umi-production 36 | .umi-test 37 | .dumi/tmp* 38 | 39 | # husky 40 | .husky/prepare-commit-msg 41 | 42 | # misc 43 | # add other ignore file below 44 | bun.lockb 45 | .vercel 46 | .env 47 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | npx --no-install commitlint --edit $1 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | npx --no-install lint-staged 4 | -------------------------------------------------------------------------------- /.i18nrc.js: -------------------------------------------------------------------------------- 1 | const { description } = require('./package.json'); 2 | const { defineConfig } = require('@lobehub/i18n-cli'); 3 | 4 | module.exports = defineConfig({ 5 | reference: description, 6 | entry: 'public/locales/en', 7 | entryLocale: 'en', 8 | output: 'public/locales', 9 | outputLocales: ['cn'], 10 | splitToken: 2500, 11 | temperature: 0, 12 | modelName: 'gpt-3.5-turbo', 13 | }); 14 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | lockfile=false 2 | resolution-mode=highest 3 | public-hoist-pattern[]=*@umijs/lint* 4 | public-hoist-pattern[]=*changelog* 5 | public-hoist-pattern[]=*commitlint* 6 | public-hoist-pattern[]=*eslint* 7 | public-hoist-pattern[]=*postcss* 8 | public-hoist-pattern[]=*prettier* 9 | public-hoist-pattern[]=*remark* 10 | public-hoist-pattern[]=*semantic-release* 11 | public-hoist-pattern[]=*stylelint* 12 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Prettierignore for LobeHub 2 | ################################################################ 3 | 4 | # general 5 | .DS_Store 6 | .editorconfig 7 | .idea 8 | .vscode 9 | .history 10 | .temp 11 | .env.local 12 | .husky 13 | .npmrc 14 | .gitkeep 15 | venv 16 | temp 17 | tmp 18 | LICENSE 19 | 20 | # dependencies 21 | node_modules 22 | *.log 23 | *.lock 24 | package-lock.json 25 | 26 | # ci 27 | coverage 28 | .coverage 29 | .eslintcache 30 | .stylelintcache 31 | test-output 32 | __snapshots__ 33 | *.snap 34 | 35 | # production 36 | dist 37 | es 38 | logs 39 | 40 | # umi 41 | .umi 42 | .umi-production 43 | .umi-test 44 | .dumi/tmp* 45 | 46 | # ignore files 47 | .*ignore 48 | 49 | # docker 50 | docker 51 | Dockerfile* 52 | 53 | # image 54 | *.webp 55 | *.gif 56 | *.png 57 | *.jpg 58 | *.svg 59 | 60 | # misc 61 | # add other ignore file below 62 | .next -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@lobehub/lint').prettier; 2 | -------------------------------------------------------------------------------- /.releaserc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@lobehub/lint').semanticRelease; 2 | -------------------------------------------------------------------------------- /.remarkrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@lobehub/lint').remarklint; 2 | -------------------------------------------------------------------------------- /.stylelintrc.js: -------------------------------------------------------------------------------- 1 | const config = require('@lobehub/lint').stylelint; 2 | 3 | module.exports = { 4 | ...config, 5 | rules: { 6 | 'selector-id-pattern': null, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Changelog 4 | 5 | ### [Version 0.10.4](https://github.com/lobehub/lobe-readme-wizard/compare/v0.10.3...v0.10.4) 6 | 7 | Released on **2025-02-07** 8 | 9 | #### 🐛 Bug Fixes 10 | 11 | - **misc**: Fix build, Fix build. 12 | 13 |
14 | 15 |
16 | Improvements and Fixes 17 | 18 | #### What's fixed 19 | 20 | - **misc**: Fix build ([730a9c0](https://github.com/lobehub/lobe-readme-wizard/commit/730a9c0)) 21 | - **misc**: Fix build ([427bbad](https://github.com/lobehub/lobe-readme-wizard/commit/427bbad)) 22 | 23 |
24 | 25 |
26 | 27 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 28 | 29 |
30 | 31 | ### [Version 0.10.3](https://github.com/lobehub/lobe-readme-wizard/compare/v0.10.2...v0.10.3) 32 | 33 | Released on **2025-02-07** 34 | 35 | #### 🐛 Bug Fixes 36 | 37 | - **misc**: Fix demo. 38 | 39 |
40 | 41 |
42 | Improvements and Fixes 43 | 44 | #### What's fixed 45 | 46 | - **misc**: Fix demo ([7487649](https://github.com/lobehub/lobe-readme-wizard/commit/7487649)) 47 | 48 |
49 | 50 |
51 | 52 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 53 | 54 |
55 | 56 | ### [Version 0.10.2](https://github.com/lobehub/lobe-readme-wizard/compare/v0.10.1...v0.10.2) 57 | 58 | Released on **2025-02-07** 59 | 60 | #### 🐛 Bug Fixes 61 | 62 | - **misc**: Fix sponsor kit. 63 | 64 |
65 | 66 |
67 | Improvements and Fixes 68 | 69 | #### What's fixed 70 | 71 | - **misc**: Fix sponsor kit ([5c2e77d](https://github.com/lobehub/lobe-readme-wizard/commit/5c2e77d)) 72 | 73 |
74 | 75 |
76 | 77 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 78 | 79 |
80 | 81 | ### [Version 0.10.1](https://github.com/lobehub/lobe-readme-wizard/compare/v0.10.0...v0.10.1) 82 | 83 | Released on **2024-11-24** 84 | 85 | #### 💄 Styles 86 | 87 | - **misc**: Update og. 88 | 89 |
90 | 91 |
92 | Improvements and Fixes 93 | 94 | #### Styles 95 | 96 | - **misc**: Update og ([9586724](https://github.com/lobehub/lobe-readme-wizard/commit/9586724)) 97 | 98 |
99 | 100 |
101 | 102 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 103 | 104 |
105 | 106 | ## [Version 0.10.0](https://github.com/lobehub/lobe-readme-wizard/compare/v0.9.4...v0.10.0) 107 | 108 | Released on **2024-09-20** 109 | 110 | #### ✨ Features 111 | 112 | - **misc**: Update sitemap. 113 | 114 |
115 | 116 |
117 | Improvements and Fixes 118 | 119 | #### What's improved 120 | 121 | - **misc**: Update sitemap ([4fd8614](https://github.com/lobehub/lobe-readme-wizard/commit/4fd8614)) 122 | 123 |
124 | 125 |
126 | 127 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 128 | 129 |
130 | 131 | ### [Version 0.9.4](https://github.com/lobehub/lobe-readme-wizard/compare/v0.9.3...v0.9.4) 132 | 133 | Released on **2024-06-25** 134 | 135 | #### 🐛 Bug Fixes 136 | 137 | - **misc**: Add cache. 138 | 139 |
140 | 141 |
142 | Improvements and Fixes 143 | 144 | #### What's fixed 145 | 146 | - **misc**: Add cache ([3012739](https://github.com/lobehub/lobe-readme-wizard/commit/3012739)) 147 | 148 |
149 | 150 |
151 | 152 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 153 | 154 |
155 | 156 | ### [Version 0.9.3](https://github.com/lobehub/lobe-readme-wizard/compare/v0.9.2...v0.9.3) 157 | 158 | Released on **2024-05-16** 159 | 160 | #### 🐛 Bug Fixes 161 | 162 | - **misc**: Fix dumi. 163 | 164 |
165 | 166 |
167 | Improvements and Fixes 168 | 169 | #### What's fixed 170 | 171 | - **misc**: Fix dumi ([7ca0012](https://github.com/lobehub/lobe-readme-wizard/commit/7ca0012)) 172 | 173 |
174 | 175 |
176 | 177 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 178 | 179 |
180 | 181 | ### [Version 0.9.2](https://github.com/lobehub/lobe-readme-wizard/compare/v0.9.1...v0.9.2) 182 | 183 | Released on **2024-04-30** 184 | 185 | #### 🐛 Bug Fixes 186 | 187 | - **misc**: Fix api import. 188 | 189 |
190 | 191 |
192 | Improvements and Fixes 193 | 194 | #### What's fixed 195 | 196 | - **misc**: Fix api import ([a0dc87f](https://github.com/lobehub/lobe-readme-wizard/commit/a0dc87f)) 197 | 198 |
199 | 200 |
201 | 202 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 203 | 204 |
205 | 206 | ### [Version 0.9.1](https://github.com/lobehub/lobe-readme-wizard/compare/v0.9.0...v0.9.1) 207 | 208 | Released on **2024-04-30** 209 | 210 | #### 🐛 Bug Fixes 211 | 212 | - **misc**: Fix deps. 213 | 214 |
215 | 216 |
217 | Improvements and Fixes 218 | 219 | #### What's fixed 220 | 221 | - **misc**: Fix deps ([2c92216](https://github.com/lobehub/lobe-readme-wizard/commit/2c92216)) 222 | 223 |
224 | 225 |
226 | 227 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 228 | 229 |
230 | 231 | ## [Version 0.9.0](https://github.com/lobehub/lobe-readme-wizard/compare/v0.8.10...v0.9.0) 232 | 233 | Released on **2024-04-29** 234 | 235 | #### ✨ Features 236 | 237 | - **misc**: Update Sponsor provider. 238 | 239 |
240 | 241 |
242 | Improvements and Fixes 243 | 244 | #### What's improved 245 | 246 | - **misc**: Update Sponsor provider ([61ca14b](https://github.com/lobehub/lobe-readme-wizard/commit/61ca14b)) 247 | 248 |
249 | 250 |
251 | 252 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 253 | 254 |
255 | 256 | ### [Version 0.8.10](https://github.com/lobehub/lobe-readme-wizard/compare/v0.8.9...v0.8.10) 257 | 258 | Released on **2023-12-25** 259 | 260 | #### 🐛 Bug Fixes 261 | 262 | - **misc**: Fix sponsor fliter. 263 | 264 |
265 | 266 |
267 | Improvements and Fixes 268 | 269 | #### What's fixed 270 | 271 | - **misc**: Fix sponsor fliter ([3a4cd47](https://github.com/lobehub/lobe-readme-wizard/commit/3a4cd47)) 272 | 273 |
274 | 275 |
276 | 277 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 278 | 279 |
280 | 281 | ### [Version 0.8.9](https://github.com/lobehub/lobe-readme-wizard/compare/v0.8.8...v0.8.9) 282 | 283 | Released on **2023-12-23** 284 | 285 | #### 🐛 Bug Fixes 286 | 287 | - **misc**: Fix func typo. 288 | 289 |
290 | 291 |
292 | Improvements and Fixes 293 | 294 | #### What's fixed 295 | 296 | - **misc**: Fix func typo ([5a86506](https://github.com/lobehub/lobe-readme-wizard/commit/5a86506)) 297 | 298 |
299 | 300 |
301 | 302 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 303 | 304 |
305 | 306 | ### [Version 0.8.8](https://github.com/lobehub/lobe-readme-wizard/compare/v0.8.7...v0.8.8) 307 | 308 | Released on **2023-12-23** 309 | 310 | #### 🐛 Bug Fixes 311 | 312 | - **misc**: Fix SponsorKit. 313 | 314 |
315 | 316 |
317 | Improvements and Fixes 318 | 319 | #### What's fixed 320 | 321 | - **misc**: Fix SponsorKit ([d1e3ecb](https://github.com/lobehub/lobe-readme-wizard/commit/d1e3ecb)) 322 | 323 |
324 | 325 |
326 | 327 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 328 | 329 |
330 | 331 | ### [Version 0.8.7](https://github.com/lobehub/lobe-readme-wizard/compare/v0.8.6...v0.8.7) 332 | 333 | Released on **2023-12-06** 334 | 335 | #### 💄 Styles 336 | 337 | - **misc**: Update sponsor style. 338 | 339 |
340 | 341 |
342 | Improvements and Fixes 343 | 344 | #### Styles 345 | 346 | - **misc**: Update sponsor style ([cddd270](https://github.com/lobehub/lobe-readme-wizard/commit/cddd270)) 347 | 348 |
349 | 350 |
351 | 352 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 353 | 354 |
355 | 356 | ### [Version 0.8.6](https://github.com/lobehub/lobe-readme-wizard/compare/v0.8.5...v0.8.6) 357 | 358 | Released on **2023-12-06** 359 | 360 | #### 💄 Styles 361 | 362 | - **misc**: Add auto height to sponsor. 363 | 364 |
365 | 366 |
367 | Improvements and Fixes 368 | 369 | #### Styles 370 | 371 | - **misc**: Add auto height to sponsor ([e1cbb69](https://github.com/lobehub/lobe-readme-wizard/commit/e1cbb69)) 372 | 373 |
374 | 375 |
376 | 377 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 378 | 379 |
380 | 381 | ### [Version 0.8.5](https://github.com/lobehub/lobe-readme-wizard/compare/v0.8.4...v0.8.5) 382 | 383 | Released on **2023-12-06** 384 | 385 | #### 🐛 Bug Fixes 386 | 387 | - **misc**: Fix size. 388 | 389 |
390 | 391 |
392 | Improvements and Fixes 393 | 394 | #### What's fixed 395 | 396 | - **misc**: Fix size ([0f00424](https://github.com/lobehub/lobe-readme-wizard/commit/0f00424)) 397 | 398 |
399 | 400 |
401 | 402 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 403 | 404 |
405 | 406 | ### [Version 0.8.4](https://github.com/lobehub/lobe-readme-wizard/compare/v0.8.3...v0.8.4) 407 | 408 | Released on **2023-12-06** 409 | 410 | #### 🐛 Bug Fixes 411 | 412 | - **misc**: Fix og. 413 | 414 |
415 | 416 |
417 | Improvements and Fixes 418 | 419 | #### What's fixed 420 | 421 | - **misc**: Fix og ([703f45a](https://github.com/lobehub/lobe-readme-wizard/commit/703f45a)) 422 | 423 |
424 | 425 |
426 | 427 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 428 | 429 |
430 | 431 | ### [Version 0.8.3](https://github.com/lobehub/lobe-readme-wizard/compare/v0.8.2...v0.8.3) 432 | 433 | Released on **2023-12-06** 434 | 435 | #### 🐛 Bug Fixes 436 | 437 | - **misc**: Fix @vercel/og. 438 | 439 |
440 | 441 |
442 | Improvements and Fixes 443 | 444 | #### What's fixed 445 | 446 | - **misc**: Fix @vercel/og ([d198d99](https://github.com/lobehub/lobe-readme-wizard/commit/d198d99)) 447 | 448 |
449 | 450 |
451 | 452 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 453 | 454 |
455 | 456 | ### [Version 0.8.2](https://github.com/lobehub/lobe-readme-wizard/compare/v0.8.1...v0.8.2) 457 | 458 | Released on **2023-12-06** 459 | 460 | #### 🐛 Bug Fixes 461 | 462 | - **misc**: Debug. 463 | 464 |
465 | 466 |
467 | Improvements and Fixes 468 | 469 | #### What's fixed 470 | 471 | - **misc**: Debug ([d064561](https://github.com/lobehub/lobe-readme-wizard/commit/d064561)) 472 | 473 |
474 | 475 |
476 | 477 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 478 | 479 |
480 | 481 | ### [Version 0.8.1](https://github.com/lobehub/lobe-readme-wizard/compare/v0.8.0...v0.8.1) 482 | 483 | Released on **2023-12-06** 484 | 485 | #### 🐛 Bug Fixes 486 | 487 | - **misc**: Fix import. 488 | 489 |
490 | 491 |
492 | Improvements and Fixes 493 | 494 | #### What's fixed 495 | 496 | - **misc**: Fix import ([ba6dd2b](https://github.com/lobehub/lobe-readme-wizard/commit/ba6dd2b)) 497 | 498 |
499 | 500 |
501 | 502 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 503 | 504 |
505 | 506 | ## [Version 0.8.0](https://github.com/lobehub/lobe-readme-wizard/compare/v0.7.2...v0.8.0) 507 | 508 | Released on **2023-12-06** 509 | 510 | #### ✨ Features 511 | 512 | - **misc**: Add SponsorKit. 513 | 514 |
515 | 516 |
517 | Improvements and Fixes 518 | 519 | #### What's improved 520 | 521 | - **misc**: Add SponsorKit ([4f38bc4](https://github.com/lobehub/lobe-readme-wizard/commit/4f38bc4)) 522 | 523 |
524 | 525 |
526 | 527 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 528 | 529 |
530 | 531 | ### [Version 0.7.2](https://github.com/lobehub/lobe-readme-wizard/compare/v0.7.1...v0.7.2) 532 | 533 | Released on **2023-11-16** 534 | 535 | #### 🐛 Bug Fixes 536 | 537 | - **misc**: Fix build. 538 | 539 |
540 | 541 |
542 | Improvements and Fixes 543 | 544 | #### What's fixed 545 | 546 | - **misc**: Fix build ([ef162a3](https://github.com/lobehub/lobe-readme-wizard/commit/ef162a3)) 547 | 548 |
549 | 550 |
551 | 552 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 553 | 554 |
555 | 556 | ### [Version 0.7.1](https://github.com/lobehub/lobe-readme-wizard/compare/v0.7.0...v0.7.1) 557 | 558 | Released on **2023-10-11** 559 | 560 | #### 💄 Styles 561 | 562 | - **misc**: Update docker shields style. 563 | 564 |
565 | 566 |
567 | Improvements and Fixes 568 | 569 | #### Styles 570 | 571 | - **misc**: Update docker shields style ([7853b56](https://github.com/lobehub/lobe-readme-wizard/commit/7853b56)) 572 | 573 |
574 | 575 |
576 | 577 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 578 | 579 |
580 | 581 | ## [Version 0.7.0](https://github.com/lobehub/lobe-readme-wizard/compare/v0.6.1...v0.7.0) 582 | 583 | Released on **2023-10-11** 584 | 585 | #### ✨ Features 586 | 587 | - **misc**: Add docker shield. 588 | 589 |
590 | 591 |
592 | Improvements and Fixes 593 | 594 | #### What's improved 595 | 596 | - **misc**: Add docker shield ([c098ab0](https://github.com/lobehub/lobe-readme-wizard/commit/c098ab0)) 597 | 598 |
599 | 600 |
601 | 602 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 603 | 604 |
605 | 606 | ### [Version 0.6.1](https://github.com/lobehub/lobe-readme-wizard/compare/v0.6.0...v0.6.1) 607 | 608 | Released on **2023-09-28** 609 | 610 | #### 🐛 Bug Fixes 611 | 612 | - **misc**: Fix link typo. 613 | 614 |
615 | 616 |
617 | Improvements and Fixes 618 | 619 | #### What's fixed 620 | 621 | - **misc**: Fix link typo ([355598a](https://github.com/lobehub/lobe-readme-wizard/commit/355598a)) 622 | 623 |
624 | 625 |
626 | 627 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 628 | 629 |
630 | 631 | ## [Version 0.6.0](https://github.com/lobehub/lobe-readme-wizard/compare/v0.5.0...v0.6.0) 632 | 633 | Released on **2023-09-28** 634 | 635 | #### ✨ Features 636 | 637 | - **misc**: Add social shield. 638 | 639 |
640 | 641 |
642 | Improvements and Fixes 643 | 644 | #### What's improved 645 | 646 | - **misc**: Add social shield ([a359e24](https://github.com/lobehub/lobe-readme-wizard/commit/a359e24)) 647 | 648 |
649 | 650 |
651 | 652 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 653 | 654 |
655 | 656 | ## [Version 0.5.0](https://github.com/lobehub/lobe-readme-wizard/compare/v0.4.0...v0.5.0) 657 | 658 | Released on **2023-09-28** 659 | 660 | #### ✨ Features 661 | 662 | - **misc**: Add share shield. 663 | 664 |
665 | 666 |
667 | Improvements and Fixes 668 | 669 | #### What's improved 670 | 671 | - **misc**: Add share shield ([2deb3ad](https://github.com/lobehub/lobe-readme-wizard/commit/2deb3ad)) 672 | 673 |
674 | 675 |
676 | 677 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 678 | 679 |
680 | 681 | ## [Version 0.4.0](https://github.com/lobehub/lobe-readme-wizard/compare/v0.3.1...v0.4.0) 682 | 683 | Released on **2023-09-28** 684 | 685 | #### ✨ Features 686 | 687 | - **misc**: Add bilibili shield. 688 | 689 |
690 | 691 |
692 | Improvements and Fixes 693 | 694 | #### What's improved 695 | 696 | - **misc**: Add bilibili shield ([ab1b9da](https://github.com/lobehub/lobe-readme-wizard/commit/ab1b9da)) 697 | 698 |
699 | 700 |
701 | 702 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 703 | 704 |
705 | 706 | ### [Version 0.3.1](https://github.com/lobehub/lobe-readme-wizard/compare/v0.3.0...v0.3.1) 707 | 708 | Released on **2023-09-18** 709 | 710 | #### 💄 Styles 711 | 712 | - **misc**: Fix style. 713 | 714 |
715 | 716 |
717 | Improvements and Fixes 718 | 719 | #### Styles 720 | 721 | - **misc**: Fix style ([949c7f1](https://github.com/lobehub/lobe-readme-wizard/commit/949c7f1)) 722 | 723 |
724 | 725 |
726 | 727 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 728 | 729 |
730 | 731 | ## [Version 0.3.0](https://github.com/lobehub/lobe-readme-wizard/compare/v0.2.1...v0.3.0) 732 | 733 | Released on **2023-09-18** 734 | 735 | #### ♻ Code Refactoring 736 | 737 | - **misc**: Refactor to dumi. 738 | 739 | #### ✨ Features 740 | 741 | - **misc**: Update demos. 742 | 743 | #### 🐛 Bug Fixes 744 | 745 | - **misc**: Fix typo. 746 | 747 |
748 | 749 |
750 | Improvements and Fixes 751 | 752 | #### Code refactoring 753 | 754 | - **misc**: Refactor to dumi ([ee3cd5f](https://github.com/lobehub/lobe-readme-wizard/commit/ee3cd5f)) 755 | 756 | #### What's improved 757 | 758 | - **misc**: Update demos ([1515782](https://github.com/lobehub/lobe-readme-wizard/commit/1515782)) 759 | 760 | #### What's fixed 761 | 762 | - **misc**: Fix typo ([d0e4c08](https://github.com/lobehub/lobe-readme-wizard/commit/d0e4c08)) 763 | 764 |
765 | 766 |
767 | 768 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 769 | 770 |
771 | 772 | ### [Version 0.2.1](https://github.com/lobehub/lobe-readme-generator/compare/v0.2.0...v0.2.1) 773 | 774 | Released on **2023-09-18** 775 | 776 | #### 🐛 Bug Fixes 777 | 778 | - **misc**: Simple icon. 779 | 780 |
781 | 782 |
783 | Improvements and Fixes 784 | 785 | #### What's fixed 786 | 787 | - **misc**: Simple icon ([0bb612c](https://github.com/lobehub/lobe-readme-generator/commit/0bb612c)) 788 | 789 |
790 | 791 |
792 | 793 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 794 | 795 |
796 | 797 | ## [Version 0.2.0](https://github.com/lobehub/lobe-readme-generator/compare/v0.1.0...v0.2.0) 798 | 799 | Released on **2023-09-17** 800 | 801 | #### ✨ Features 802 | 803 | - **misc**: Add readme generator. 804 | 805 |
806 | 807 |
808 | Improvements and Fixes 809 | 810 | #### What's improved 811 | 812 | - **misc**: Add readme generator ([aad748d](https://github.com/lobehub/lobe-readme-generator/commit/aad748d)) 813 | 814 |
815 | 816 |
817 | 818 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 819 | 820 |
821 | 822 | ## [Version 0.1.0](https://github.com/lobehub/lobe-readme-generator/compare/v0.0.1...v0.1.0) 823 | 824 | Released on **2023-09-17** 825 | 826 | #### ✨ Features 827 | 828 | - **misc**: Add shield editor. 829 | 830 | #### 🐛 Bug Fixes 831 | 832 | - **misc**: Fix ci, Fix next version, Fix scripts. 833 | 834 |
835 | 836 |
837 | Improvements and Fixes 838 | 839 | #### What's improved 840 | 841 | - **misc**: Add shield editor ([0102c80](https://github.com/lobehub/lobe-readme-generator/commit/0102c80)) 842 | 843 | #### What's fixed 844 | 845 | - **misc**: Fix ci ([1b3d3eb](https://github.com/lobehub/lobe-readme-generator/commit/1b3d3eb)) 846 | - **misc**: Fix next version ([b8e3e7f](https://github.com/lobehub/lobe-readme-generator/commit/b8e3e7f)) 847 | - **misc**: Fix scripts ([dea5c8c](https://github.com/lobehub/lobe-readme-generator/commit/dea5c8c)) 848 | 849 |
850 | 851 |
852 | 853 | [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top) 854 | 855 |
856 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 LobeHub 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 |

Readme Wizard

10 | 11 | Generate lobe style product README _by MAGIC_ \~ 🪄 12 | 13 | [Documents](https://readme-wizard.lobehub.com) · [Changelog](./CHANGELOG.md) · [Report Bug][github-issues-link] · [Request Feature][github-issues-link] 14 | 15 | [![][github-release-shield]][github-release-link] 16 | [![][vecel-shield]][vecel-link] 17 | [![][discord-shield]][discord-link] 18 | [![][github-releasedate-shield]][github-releasedate-link] 19 | [![][github-action-test-shield]][github-action-test-link] 20 | [![][github-action-release-shield]][github-action-release-link]
21 | [![][github-contributors-shield]][github-contributors-link] 22 | [![][github-forks-shield]][github-forks-link] 23 | [![][github-stars-shield]][github-stars-link] 24 | [![][github-issues-shield]][github-issues-link] 25 | [![][github-license-shield]][github-license-link] 26 | 27 | ![](https://github-production-user-asset-6210df.s3.amazonaws.com/17870709/268598243-0db9877a-ae3c-4250-8615-7d8b4f695169.png) 28 | 29 |
30 | 31 |
32 | Table of contents 33 | 34 | #### TOC 35 | 36 | - [✨ Features](#-features) 37 | 38 | - [⌨️ Local Development](#️-local-development) 39 | 40 | - [🤝 Contributing](#-contributing) 41 | 42 | - [🔗 Links](#-links) 43 | 44 | - [Credits](#credits) 45 | - [More Products](#more-products) 46 | - [Design Resources](#design-resources) 47 | - [Development Resources](#development-resources) 48 | 49 | #### 50 | 51 |
52 | 53 | ## ✨ Features 54 | 55 | - [x] 📝 **Readme Generator** 56 | - [x] Hero 57 | - [x] Features 58 | - [x] Installation 59 | - [x] Development 60 | - [x] Contributing 61 | - [x] Credits 62 | - [x] License 63 | - [x] 🔖 **Shields Generator** 64 | - [x] Custom 65 | - [x] Website 66 | - [x] Github 67 | - [x] NPM 68 | - [x] Vercel 69 | - [x] Discord 70 | - [ ] 🌏 i18n template support 71 | - [ ] 📦 Generator by `package.json` 72 | - [ ] 🪄 Generator features by openAI 73 | - [ ] ⌨️ CLI tool support 74 | 75 |
76 | 77 | [![][back-to-top]](#readme-top) 78 | 79 |
80 | 81 | ## ⌨️ Local Development 82 | 83 | You can use Github Codespaces for online development: 84 | 85 | [![][codespaces-shield]][codespaces-link] 86 | 87 | Or clone it for local development: 88 | 89 | [![][bun-shield]][bun-link] 90 | 91 | ```bash 92 | $ git clone https://github.com/lobehub/lobe-readme-wizard.git 93 | $ cd lobe-readme-wizard 94 | $ bun install 95 | $ bun dev 96 | ``` 97 | 98 |
99 | 100 | [![][back-to-top]](#readme-top) 101 | 102 |
103 | 104 | ## 🤝 Contributing 105 | 106 | Contributions of all types are more than welcome, if you are interested in contributing code, feel free to check out our GitHub [Issues][github-issues-link] to get stuck in to show us what you’re made of. 107 | 108 | [![][pr-welcome-shield]][pr-welcome-link] 109 | 110 | [![][github-contrib-shield]][github-contrib-link] 111 | 112 |
113 | 114 | [![][back-to-top]](#readme-top) 115 | 116 |
117 | 118 | ## 🔗 Links 119 | 120 | ### Credits 121 | 122 | - **recharts** - 123 | - **tremor** - 124 | - **react-activity-calendar** - 125 | 126 | ### More Products 127 | 128 | - **[🤯 Lobe Chat](https://github.com/lobehub/lobe-chat)** - An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application. 129 | - **[🅰️ Lobe Theme](https://github.com/lobehub/sd-webui-lobe-theme)** - The modern theme for stable diffusion webui, exquisite interface design, highly customizable UI, and efficiency boosting features. 130 | - **[🧸 Lobe Vidol](https://github.com/lobehub/lobe-vidol)** - Experience the magic of virtual idol creation with Lobe Vidol, enjoy the elegance of our Exquisite UI Design, dance along using MMD Dance Support, and engage in Smooth Conversations. 131 | 132 | ### Design Resources 133 | 134 | - **[🍭 Lobe UI](https://ui.lobehub.com)** - An open-source UI component library for building AIGC web apps. 135 | - **[🥨 Lobe Icons](https://lobehub.com/icons)** - Popular AI / LLM Model Brand SVG Logo and Icon Collection. 136 | - **[📊 Lobe Charts](https://charts.lobehub.com)** - React modern charts components built on recharts 137 | 138 | ### Development Resources 139 | 140 | - **[🎤 Lobe TTS](https://tts.lobehub.com)** - A high-quality & reliable TTS/STT library for Server and Browser 141 | - **[🌏 Lobe i18n](https://github.com/lobehub/lobe-cli-toolbox/blob/master/packages/lobe-i18n)** - Automation ai tool for the i18n (internationalization) translation process. 142 | 143 | [More Resources](https://lobehub.com/resources) 144 | 145 |
146 | 147 | [![][back-to-top]](#readme-top) 148 | 149 |
150 | 151 | --- 152 | 153 | #### 📝 License 154 | 155 | Copyright © 2023 [LobeHub][profile-link].
156 | This project is [MIT](./LICENSE) licensed. 157 | 158 | > _Generate by [🧙‍♂️ Readme Wizard](https://github.com/lobehub/lobe-readme-wizard 'Generate lobe style product README by MAGIC')_ 159 | 160 | 161 | 162 | [back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-black?style=flat-square 163 | [bun-link]: https://bun.sh 164 | [bun-shield]: https://img.shields.io/badge/-speedup%20with%20bun-black?logo=bun&style=for-the-badge 165 | [codespaces-link]: https://codespaces.new/lobehub/lobe-readme-wizard 166 | [codespaces-shield]: https://github.com/codespaces/badge.svg 167 | [discord-link]: https://discord.gg/AYFPHvv2jT 168 | [discord-shield]: https://img.shields.io/discord/1127171173982154893?color=5865F2&label=discord&labelColor=black&logo=discord&logoColor=white&style=flat-square 169 | [github-action-release-link]: https://github.com/lobehub/lobe-readme-wizard/actions/workflows/release.yml 170 | [github-action-release-shield]: https://img.shields.io/github/actions/workflow/status/lobehub/lobe-readme-wizard/release.yml?label=release&labelColor=black&logo=githubactions&logoColor=white&style=flat-square 171 | [github-action-test-link]: https://github.com/lobehub/lobe-readme-wizard/actions/workflows/test.yml 172 | [github-action-test-shield]: https://img.shields.io/github/actions/workflow/status/lobehub/lobe-readme-wizard/test.yml?label=test&labelColor=black&logo=githubactions&logoColor=white&style=flat-square 173 | [github-contrib-link]: https://github.com/lobehub/lobe-readme-wizard/graphs/contributors 174 | [github-contrib-shield]: https://contrib.rocks/image?repo=lobehub%2Flobe-readme-wizard 175 | [github-contributors-link]: https://github.com/lobehub/lobe-readme-wizard/graphs/contributors 176 | [github-contributors-shield]: https://img.shields.io/github/contributors/lobehub/lobe-readme-wizard?color=c4f042&labelColor=black&style=flat-square 177 | [github-forks-link]: https://github.com/lobehub/lobe-readme-wizard/network/members 178 | [github-forks-shield]: https://img.shields.io/github/forks/lobehub/lobe-readme-wizard?color=8ae8ff&labelColor=black&style=flat-square 179 | [github-issues-link]: https://github.com/lobehub/lobe-readme-wizard/issues 180 | [github-issues-shield]: https://img.shields.io/github/issues/lobehub/lobe-readme-wizard?color=ff80eb&labelColor=black&style=flat-square 181 | [github-license-link]: https://github.com/lobehub/lobe-readme-wizard/blob/main/LICENSE 182 | [github-license-shield]: https://img.shields.io/github/license/lobehub/lobe-readme-wizard?color=white&labelColor=black&style=flat-square 183 | [github-release-link]: https://github.com/lobehub/lobe-readme-wizard/releases 184 | [github-release-shield]: https://img.shields.io/github/v/release/lobehub/lobe-readme-wizard?logo=github&color=369eff&labelColor=black&style=flat-square 185 | [github-releasedate-link]: https://github.com/lobehub/lobe-readme-wizard/releases 186 | [github-releasedate-shield]: https://img.shields.io/github/release-date/lobehub/lobe-readme-wizard?labelColor=black&style=flat-square 187 | [github-stars-link]: https://github.com/lobehub/lobe-readme-wizard/network/stargazers 188 | [github-stars-shield]: https://img.shields.io/github/stars/lobehub/lobe-readme-wizard?color=ffcb47&labelColor=black&style=flat-square 189 | [pr-welcome-link]: https://github.com/lobehub/lobe-chat/pulls 190 | [pr-welcome-shield]: https://img.shields.io/badge/🤯_pr_welcome-%E2%86%92-ffcb47?labelColor=black&style=for-the-badge 191 | [profile-link]: https://github.com/lobehub 192 | [vecel-link]: https://lobe-readme-wizard.vercel.app 193 | [vecel-shield]: https://img.shields.io/website?down_message=offline&label=vecel&labelColor=black&logo=vercel&style=flat-square&up_message=online&url=https%3A%2F%2Flobe-readme-wizard.vercel.app 194 | -------------------------------------------------------------------------------- /clean-package.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | indent: 2, 3 | remove: ['scripts', 'lint-staged', 'devDependencies', 'publishConfig', 'clean-package'], 4 | }; 5 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Changelog 3 | description: New updates and improvements to @lobehub/ui 4 | nav: 5 | title: Changelog 6 | order: 999 7 | --- 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hero: 3 | title: Readme Wizard 4 | description: Generate lobe style product README by MAGIC ~ 🪄 5 | --- 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from 'antd'; 2 | import { Link } from 'dumi'; 3 | import { Center } from 'react-layout-kit'; 4 | 5 | import ReadmeHero from '@/ReadmeHero'; 6 | 7 | export default () => { 8 | return ( 9 |
10 | 11 | 12 | 13 | 14 |
15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lobehub/readme-wizard", 3 | "version": "0.10.4", 4 | "description": "Generate lobe style product README by magic", 5 | "homepage": "https://github.com/lobehub/lobe-readme-generator", 6 | "bugs": { 7 | "url": "https://github.com/lobehub/lobe-readme-wizard/issues/new/choose" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/lobehub/lobe-readme-wizard.git" 12 | }, 13 | "license": "MIT", 14 | "author": "LobeHub ", 15 | "sideEffects": false, 16 | "main": "es/index.js", 17 | "module": "es/index.js", 18 | "types": "es/index.d.ts", 19 | "files": [ 20 | "es" 21 | ], 22 | "scripts": { 23 | "build": "father build", 24 | "build:watch": "father dev", 25 | "ci": "npm run lint", 26 | "dev": "dumi dev", 27 | "docs:build": "dumi build", 28 | "docs:build-analyze": "ANALYZE=1 dumi build", 29 | "docs:dev": "dumi dev", 30 | "doctor": "father doctor", 31 | "i18n": "lobe-i18n", 32 | "icon-sync": "node ./scripts/syncSimpleIconList.mjs", 33 | "lint": "npm run lint:ts && npm run lint:style", 34 | "lint:md": "remark . --quiet --frail --output", 35 | "lint:style": "stylelint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix", 36 | "lint:ts": "eslint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix", 37 | "prepack": "clean-package", 38 | "postpack": "clean-package restore", 39 | "prepare": "husky && npm run setup", 40 | "prettier": "prettier -c --write \"**/**\"", 41 | "pull": "git pull", 42 | "release": "semantic-release", 43 | "setup": "dumi setup", 44 | "start": "next start", 45 | "stylelint": "stylelint \"src/**/*.{js,jsx,ts,tsx}\" --fix", 46 | "test": "vitest --passWithNoTests", 47 | "test:coverage": "vitest run --coverage --passWithNoTests", 48 | "test:update": "vitest -u", 49 | "type-check": "tsc --noEmit" 50 | }, 51 | "lint-staged": { 52 | "*.md": [ 53 | "remark --quiet --output --", 54 | "prettier --write --no-error-on-unmatched-pattern" 55 | ], 56 | "*.json": [ 57 | "prettier --write --no-error-on-unmatched-pattern" 58 | ], 59 | "*.{js,jsx}": [ 60 | "prettier --write", 61 | "stylelint --fix", 62 | "eslint --fix" 63 | ], 64 | "*.{ts,tsx}": [ 65 | "prettier --parser=typescript --write", 66 | "stylelint --fix", 67 | "eslint --fix" 68 | ] 69 | }, 70 | "dependencies": { 71 | "@babel/runtime": "^7.26.7", 72 | "@lobehub/ui": "^1.164.11", 73 | "@vercel/og": "~0.6.5", 74 | "ahooks": "^3.8.4", 75 | "antd": "^5.23.4", 76 | "antd-style": "^3.7.1", 77 | "dotenv": "^16.4.7", 78 | "fast-deep-equal": "^3.1.3", 79 | "immer": "^10.1.1", 80 | "leva": "^0.10.0", 81 | "lodash-es": "^4.17.21", 82 | "lucide-react": "^0.396.0", 83 | "node-html-parser": "^6.1.13", 84 | "polished": "^4.3.1", 85 | "query-string": "^9.1.1", 86 | "react": "^19.0.0", 87 | "react-dom": "^19.0.0", 88 | "react-layout-kit": "^1.9.1", 89 | "react-markdown": "^9.0.3", 90 | "react-syntax-highlighter": "^15.6.1", 91 | "rehype-highlight": "^7.0.2", 92 | "rehype-raw": "^7.0.0", 93 | "remark-gfm": "^4.0.0", 94 | "remark-toc": "^9.0.0", 95 | "simple-icons": "^10.4.0", 96 | "swr": "^2.3.2", 97 | "url-join": "^5.0.0", 98 | "use-merge-value": "^1.2.0", 99 | "utility-types": "^3.11.0", 100 | "zustand": "^4.5.6", 101 | "zustand-utils": "^1.3.2" 102 | }, 103 | "devDependencies": { 104 | "@commitlint/cli": "^19.7.1", 105 | "@lobehub/lint": "^1.25.7", 106 | "@testing-library/react": "^14.3.1", 107 | "@types/lodash-es": "^4.17.12", 108 | "@types/react": "^19.0.8", 109 | "@types/react-dom": "^19.0.3", 110 | "@vitest/coverage-v8": "~1.2.2", 111 | "babel-plugin-antd-style": "^1.0.4", 112 | "clean-package": "^2.2.0", 113 | "commitlint": "^19.7.1", 114 | "concurrently": "^9.1.2", 115 | "cross-env": "^7.0.3", 116 | "dumi": "^2.4.17", 117 | "dumi-theme-lobehub": "^1.15.0", 118 | "eslint": "^8.57.1", 119 | "father": "^4.5.2", 120 | "husky": "^9.1.7", 121 | "jsdom": "^26.0.0", 122 | "lint-staged": "^15.4.3", 123 | "prettier": "^3.4.2", 124 | "remark": "^15.0.1", 125 | "remark-cli": "^12.0.1", 126 | "semantic-release": "^21.1.2", 127 | "stylelint": "^15.11.0", 128 | "typescript": "^5.7.3", 129 | "vitest": "~1.2.2" 130 | }, 131 | "publishConfig": { 132 | "access": "public", 133 | "registry": "https://registry.npmjs.org" 134 | }, 135 | "pnpm": { 136 | "overrides": { 137 | "mdast-util-gfm": "3.0.0" 138 | } 139 | }, 140 | "overrides": { 141 | "mdast-util-gfm": "3.0.0" 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-Agent: * 2 | Allow: / 3 | 4 | Host: https://readme-wizard.lobehub.com 5 | Sitemap: https://readme-wizard.lobehub.com/sitemap.xml 6 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "automerge": false, 4 | "dependencyDashboard": true, 5 | "ignoreDeps": [], 6 | "labels": ["dependencies"], 7 | "postUpdateOptions": ["yarnDedupeHighest"], 8 | "prConcurrentLimit": 30, 9 | "prHourlyLimit": 0, 10 | "rebaseWhen": "conflicted", 11 | "schedule": "on sunday before 6:00am", 12 | "timezone": "UTC" 13 | } 14 | -------------------------------------------------------------------------------- /scripts/syncSimpleIconList.mjs: -------------------------------------------------------------------------------- 1 | import { writeFileSync } from 'node:fs'; 2 | import { resolve } from 'node:path'; 3 | import { getIconsData, titleToSlug } from 'simple-icons/sdk'; 4 | 5 | const runIconSync = async () => { 6 | const data = await getIconsData(); 7 | const list = data.map((icon) => titleToSlug(icon.title)); 8 | writeFileSync( 9 | resolve('./src/const/icons.ts'), 10 | `export default ${JSON.stringify(Array.from(new Set(list.filter(Boolean))))} as const`, 11 | 'utf8', 12 | ); 13 | }; 14 | 15 | runIconSync(); 16 | -------------------------------------------------------------------------------- /src/Readme/share.ts: -------------------------------------------------------------------------------- 1 | import { folder } from 'leva'; 2 | import { cloneDeep, pick } from 'lodash-es'; 3 | 4 | import { shieldBaseControls } from '@/const/shieldBaseControls'; 5 | 6 | export const defaultControls = { 7 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 8 | owner: 'lobehub', 9 | repo: 'lobe-chat', 10 | /* eslint-enable */ 11 | }; 12 | 13 | export const defaultControlsExtra = { 14 | ['⚒️']: folder(pick(cloneDeep(shieldBaseControls), ['color', 'labelColor', 'style']), { 15 | collapsed: true, 16 | }), 17 | }; 18 | -------------------------------------------------------------------------------- /src/ReadmeContributing/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: readme 4 | title: Contributing 5 | order: 6 6 | description: Contributing markdown generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ReadmeContributing/index.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import { defaultControls } from '@/Readme/share'; 5 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 6 | import { genMarkdownContributing } from '@/services/genMarkdownContributing'; 7 | 8 | const controls = { 9 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 10 | ...defaultControls, 11 | prWelcome: '🤯 PR WELCOME', 12 | backToTop: true, 13 | /* eslint-enable */ 14 | }; 15 | 16 | const Hero = memo(() => { 17 | const store = useCreateStore(); 18 | 19 | const options = useControls(controls, { store }); 20 | 21 | const md = useMemo(() => genMarkdownContributing(options), [options]); 22 | 23 | return {md.join('\n\n')}; 24 | }); 25 | 26 | export default Hero; 27 | -------------------------------------------------------------------------------- /src/ReadmeCredits/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: readme 4 | title: Credits 5 | order: 6 6 | description: Credits markdown generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ReadmeCredits/index.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo, useState } from 'react'; 3 | 4 | import MarkdownEditor from '@/components/MarkdownEditor'; 5 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 6 | import { creditsSample } from '@/const/sample'; 7 | import { genMarkdownCredits } from '@/services/genMarkdownCredits'; 8 | 9 | const controls = { 10 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 11 | title: 'Links', 12 | backToTop: true, 13 | /* eslint-enable */ 14 | }; 15 | 16 | const Credits = memo(() => { 17 | const [value, setValue] = useState(creditsSample); 18 | const store = useCreateStore(); 19 | 20 | const options = useControls(controls, { store }); 21 | 22 | const md = useMemo(() => genMarkdownCredits(options, value), [value, options]); 23 | 24 | return ( 25 | <> 26 | 27 | {md.join('\n\n')} 28 | 29 | ); 30 | }); 31 | 32 | export default Credits; 33 | -------------------------------------------------------------------------------- /src/ReadmeDevelopment/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: readme 4 | title: Development 5 | order: 4 6 | description: Development markdown generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ReadmeDevelopment/index.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import { defaultControls } from '@/Readme/share'; 5 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 6 | import { genMarkdownDevelopment } from '@/services/genMarkdownDevelopment'; 7 | 8 | const controls = { 9 | ...defaultControls, 10 | backToTop: true, 11 | bun: true, 12 | }; 13 | 14 | const Hero = memo(() => { 15 | const store = useCreateStore(); 16 | 17 | const options = useControls(controls, { store }); 18 | 19 | const md = useMemo(() => genMarkdownDevelopment(options), [options]); 20 | 21 | return {md.join('\n\n')}; 22 | }); 23 | 24 | export default Hero; 25 | -------------------------------------------------------------------------------- /src/ReadmeFeatures/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: readme 4 | title: Features 5 | order: 2 6 | description: Features markdown generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ReadmeFeatures/index.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo, useState } from 'react'; 3 | 4 | import MarkdownEditor from '@/components/MarkdownEditor'; 5 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 6 | import { featuresSample } from '@/const/sample'; 7 | import { genMarkdownFeatures } from '@/services/genMarkdownFeatures'; 8 | 9 | const controls = { 10 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 11 | title: 'Features', 12 | backToTop: true, 13 | /* eslint-enable */ 14 | }; 15 | 16 | const Features = memo(() => { 17 | const [value, setValue] = useState(featuresSample); 18 | const store = useCreateStore(); 19 | 20 | const options = useControls(controls, { store }); 21 | 22 | const md = useMemo(() => genMarkdownFeatures(options, value), [value, options]); 23 | 24 | return ( 25 | <> 26 | 27 | {md.join('\n\n')} 28 | 29 | ); 30 | }); 31 | 32 | export default Features; 33 | -------------------------------------------------------------------------------- /src/ReadmeHero/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: readme 4 | title: Hero 5 | order: 1 6 | description: Hero markdown generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ReadmeHero/index.tsx: -------------------------------------------------------------------------------- 1 | import { folder, useControls, useCreateStore } from 'leva'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import { defaultControls, defaultControlsExtra } from '@/Readme/share'; 5 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 6 | import { genMarkdownHero } from '@/services/genMarkdownHero'; 7 | 8 | const controls = { 9 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 10 | logo: 'https://registry.npmmirror.com/@lobehub/assets-logo/1.0.0/files/assets/logo-3d.webp', 11 | logo2: '', 12 | title: 'Lobe Chat', 13 | description: 14 | 'LobeChat is a open-source, extensible , high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application.', 15 | banner: 'https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png', 16 | backToTop: true, 17 | Github: folder({ ...defaultControls, branch: 'main', workflow: 'test,release' }), 18 | NPM: folder({ 19 | packageName: '@lobehub/chat', 20 | }), 21 | ...defaultControlsExtra, 22 | /* eslint-enable */ 23 | }; 24 | 25 | const Hero = memo(() => { 26 | const store = useCreateStore(); 27 | 28 | const options = useControls(controls, { store }); 29 | 30 | const md = useMemo(() => genMarkdownHero(options), [options]); 31 | 32 | return {md.join('\n\n')}; 33 | }); 34 | 35 | export default Hero; 36 | -------------------------------------------------------------------------------- /src/ReadmeInstallation/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: readme 4 | title: Installation 5 | order: 2 6 | description: Installation markdown generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ReadmeInstallation/index.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 5 | import { genMarkdownInstallation } from '@/services/genMarkdownInstallation'; 6 | 7 | const controls = { 8 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 9 | packageName: '@lobehub/ui', 10 | esm: true, 11 | bun: true, 12 | nextjs: true, 13 | backToTop: true, 14 | /* eslint-enable */ 15 | }; 16 | 17 | const Installation = memo(() => { 18 | const store = useCreateStore(); 19 | 20 | const options = useControls(controls, { store }); 21 | 22 | const md = useMemo(() => genMarkdownInstallation(options), [options]); 23 | 24 | return {md.join('\n\n')}; 25 | }); 26 | 27 | export default Installation; 28 | -------------------------------------------------------------------------------- /src/ReadmeLicense/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: readme 4 | title: License 5 | order: 7 6 | description: License markdown generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ReadmeLicense/index.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import { defaultControls } from '@/Readme/share'; 5 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 6 | import { genMarkdownLicense } from '@/services/genMarkdownLicense'; 7 | 8 | const controls = { 9 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 10 | ...defaultControls, 11 | license: 'MIT', 12 | /* eslint-enable */ 13 | }; 14 | 15 | const Hero = memo(() => { 16 | const store = useCreateStore(); 17 | 18 | const options = useControls(controls, { store }); 19 | 20 | const md = useMemo(() => genMarkdownLicense(options), [options]); 21 | 22 | return {md.join('\n\n')}; 23 | }); 24 | 25 | export default Hero; 26 | -------------------------------------------------------------------------------- /src/ShieldShare/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: shields 4 | title: Share 5 | order: 7 6 | description: Share shields generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ShieldShare/index.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { folder } from 'leva'; 3 | import { pick } from 'lodash-es'; 4 | import { memo, useMemo } from 'react'; 5 | 6 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 7 | import { shareShieldControlsPickList } from '@/const/shareShieldControls'; 8 | import { shieldBaseControls } from '@/const/shieldBaseControls'; 9 | import { genShareShields } from '@/services/genShareShield'; 10 | 11 | const controls = { 12 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 13 | title: { 14 | value: 'Check this GitHub repository out 🤯 LobeChat', 15 | rows: 4, 16 | }, 17 | desc: { 18 | value: 19 | 'An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application.', 20 | rows: 8, 21 | }, 22 | hashtags: 'chatbot, chatGPT, openAI', 23 | url: 'https://github.com/lobehub/lobe-chat', 24 | ['⚒️']: folder( 25 | { 26 | ...pick(shieldBaseControls, ['style', 'color']), 27 | labelColor: { 28 | ...shieldBaseControls.labelColor, 29 | value: 'black', 30 | }, 31 | logoColor: { 32 | ...shieldBaseControls.logoColor, 33 | value: 'white', 34 | }, 35 | }, 36 | { 37 | collapsed: true, 38 | }, 39 | ), 40 | /* eslint-enable */ 41 | }; 42 | const pickControls = { ['✅']: folder(shareShieldControlsPickList, { collapsed: true }) }; 43 | 44 | const Share = memo(() => { 45 | const store = useCreateStore(); 46 | 47 | const options = useControls(controls, { store }); 48 | const pickOptions = useControls(pickControls, { store }); 49 | 50 | const md = useMemo(() => genShareShields(options, pickOptions), [options, pickOptions]); 51 | 52 | return {md.join('\n\n')}; 53 | }); 54 | 55 | export default Share; 56 | -------------------------------------------------------------------------------- /src/ShieldsBilibili/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: shields 4 | title: Bilibili 5 | order: 6 6 | description: Bilibili shields generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ShieldsBilibili/index.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { LevaInputs, folder } from 'leva'; 3 | import { pick } from 'lodash-es'; 4 | import { memo, useMemo } from 'react'; 5 | 6 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 7 | import { shieldBaseControls } from '@/const/shieldBaseControls'; 8 | import { genBilibiliShield } from '@/services/genCustomShield'; 9 | 10 | const controls = { 11 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 12 | uid: { 13 | value: '410372', 14 | type: LevaInputs.STRING, 15 | }, 16 | label: 'followers', 17 | ['⚒️']: folder( 18 | { 19 | ...pick(shieldBaseControls, ['style', 'labelColor']), 20 | color: { 21 | ...shieldBaseControls.labelColor, 22 | value: 'fb7299', 23 | }, 24 | logoColor: { 25 | ...shieldBaseControls.logoColor, 26 | value: 'white', 27 | }, 28 | }, 29 | { 30 | collapsed: true, 31 | }, 32 | ), 33 | /* eslint-enable */ 34 | }; 35 | 36 | const Bilibili = memo(() => { 37 | const store = useCreateStore(); 38 | 39 | const options = useControls(controls, { store }); 40 | 41 | const md = useMemo(() => genBilibiliShield(options as any), [options]); 42 | 43 | return {md.join('\n\n')}; 44 | }); 45 | 46 | export default Bilibili; 47 | -------------------------------------------------------------------------------- /src/ShieldsCustom/Double.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 5 | import { shieldBaseControls } from '@/const/shieldBaseControls'; 6 | import { genCustomDoubleShield } from '@/services/genCustomShield'; 7 | 8 | import { defaultControls } from './share'; 9 | 10 | const controls = { 11 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 12 | content: 'LobeHub', 13 | labelColor: shieldBaseControls.labelColor, 14 | label: 'Readme Generator', 15 | color: { 16 | ...shieldBaseControls.color, 17 | value: 'white', 18 | }, 19 | link: 'https://github.com/lobehub/lobe-readme-wizard', 20 | ...defaultControls, 21 | /* eslint-enable */ 22 | }; 23 | 24 | const CustomDouble = memo(() => { 25 | const store = useCreateStore(); 26 | 27 | const options = useControls(controls, { store }); 28 | 29 | const md = useMemo(() => genCustomDoubleShield(options), [options]); 30 | 31 | return {md.join('\n\n')}; 32 | }); 33 | 34 | export default CustomDouble; 35 | -------------------------------------------------------------------------------- /src/ShieldsCustom/Single.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 5 | import { shieldBaseControls } from '@/const/shieldBaseControls'; 6 | import { genCustomSingleShield } from '@/services/genCustomShield'; 7 | 8 | import { defaultControls } from './share'; 9 | 10 | const controls = { 11 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 12 | label: 'Readme Generator', 13 | color: { 14 | ...shieldBaseControls.color, 15 | value: 'black', 16 | }, 17 | link: 'https://github.com/lobehub/lobe-readme-wizard', 18 | ...defaultControls, 19 | /* eslint-enable */ 20 | }; 21 | 22 | const CustomSingle = memo(() => { 23 | const store = useCreateStore(); 24 | 25 | const options = useControls(controls, { store }); 26 | 27 | const md = useMemo(() => genCustomSingleShield(options), [options]); 28 | 29 | return {md.join('\n\n')}; 30 | }); 31 | 32 | export default CustomSingle; 33 | -------------------------------------------------------------------------------- /src/ShieldsCustom/Website.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 5 | import { shieldBaseControls } from '@/const/shieldBaseControls'; 6 | import { genWebsiteShield } from '@/services/genCustomShield'; 7 | 8 | import { defaultControls } from './share'; 9 | 10 | const controls = { 11 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 12 | label: 'LobeChat', 13 | url: 'https://chat-preview.lobehub.com', 14 | up_message: 'online', 15 | down_message: 'offline', 16 | labelColor: { 17 | ...shieldBaseControls.labelColor, 18 | value: 'black', 19 | }, 20 | ...defaultControls, 21 | /* eslint-enable */ 22 | }; 23 | 24 | const Website = memo(() => { 25 | const store = useCreateStore(); 26 | 27 | const options = useControls(controls, { store }); 28 | 29 | const md = useMemo(() => genWebsiteShield(options), [options]); 30 | 31 | return {md.join('\n\n')}; 32 | }); 33 | 34 | export default Website; 35 | -------------------------------------------------------------------------------- /src/ShieldsCustom/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: shields 4 | title: Custom 5 | order: 1 6 | description: Shields generator 7 | --- 8 | 9 | ## Editor 10 | 11 | ### Custom Single 12 | 13 | 14 | 15 | ### Custom Double 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/ShieldsCustom/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as ShieldsCustomDouble } from './Double'; 2 | export { default as ShieldsCustomSingle } from './Single'; 3 | -------------------------------------------------------------------------------- /src/ShieldsCustom/share.ts: -------------------------------------------------------------------------------- 1 | import { folder } from 'leva'; 2 | import { pick } from 'lodash-es'; 3 | 4 | import { shieldBaseControls } from '@/const/shieldBaseControls'; 5 | 6 | export const defaultControls = { 7 | ['⚒️']: folder(pick(shieldBaseControls, ['logo', 'logoColor', 'style']), { 8 | collapsed: true, 9 | }), 10 | }; 11 | -------------------------------------------------------------------------------- /src/ShieldsDiscord/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: shields 4 | title: Discord 5 | order: 5 6 | description: Discord shields generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/ShieldsDiscord/index.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { LevaInputs, folder } from 'leva'; 3 | import { pick } from 'lodash-es'; 4 | import { memo, useMemo } from 'react'; 5 | 6 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 7 | import { shieldBaseControls } from '@/const/shieldBaseControls'; 8 | import { genDiscordShield } from '@/services/genCustomShield'; 9 | 10 | const controls = { 11 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 12 | serverId: { 13 | value: '1127171173982154893', 14 | type: LevaInputs.STRING, 15 | }, 16 | label: 'discord', 17 | link: 'https://discord.gg/AYFPHvv2jT', 18 | ['⚒️']: folder( 19 | { 20 | ...pick(shieldBaseControls, ['style', 'labelColor']), 21 | color: { 22 | ...shieldBaseControls.labelColor, 23 | value: '5865f2', 24 | }, 25 | logoColor: { 26 | ...shieldBaseControls.logoColor, 27 | value: 'white', 28 | }, 29 | }, 30 | { 31 | collapsed: true, 32 | }, 33 | ), 34 | /* eslint-enable */ 35 | }; 36 | 37 | const Discord = memo(() => { 38 | const store = useCreateStore(); 39 | 40 | const options = useControls(controls, { store }); 41 | 42 | const md = useMemo(() => genDiscordShield(options), [options]); 43 | 44 | return {md.join('\n\n')}; 45 | }); 46 | 47 | export default Discord; 48 | -------------------------------------------------------------------------------- /src/ShieldsDocker/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: shields 4 | title: Docker 5 | order: 4 6 | description: Docker shields generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ShieldsDocker/index.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { folder } from 'leva'; 3 | import { memo, useMemo } from 'react'; 4 | 5 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 6 | import { dockerShieldControlsPickList } from '@/const/dockerShieldControls'; 7 | import { genDockerShields } from '@/services/genDockerShield'; 8 | 9 | import { defaultControlsExtra } from './share'; 10 | 11 | const controls = defaultControlsExtra; 12 | const pickControls = { ['✅']: folder(dockerShieldControlsPickList, { collapsed: true }) }; 13 | 14 | const Docker = memo(() => { 15 | const store = useCreateStore(); 16 | 17 | const options = useControls(controls, { store }); 18 | const pickOptions = useControls(pickControls, { store }); 19 | 20 | const md = useMemo(() => genDockerShields(options, pickOptions), [options, pickOptions]); 21 | 22 | return {md.join('\n\n')}; 23 | }); 24 | 25 | export default Docker; 26 | -------------------------------------------------------------------------------- /src/ShieldsDocker/share.ts: -------------------------------------------------------------------------------- 1 | import { folder } from 'leva'; 2 | import { cloneDeep, pick } from 'lodash-es'; 3 | 4 | import { shieldBaseControls } from '@/const/shieldBaseControls'; 5 | 6 | export const defaultControls = { 7 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 8 | packageName: 'lobehub/lobe-chat', 9 | /* eslint-enable */ 10 | }; 11 | 12 | export const defaultControlsExtra = { 13 | ...defaultControls, 14 | ['⚒️']: folder(pick(cloneDeep(shieldBaseControls), ['color', 'labelColor', 'style']), { 15 | collapsed: true, 16 | }), 17 | }; 18 | -------------------------------------------------------------------------------- /src/ShieldsGithub/Action.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 5 | import { genGithubActionsShield } from '@/services/genGithubShield'; 6 | 7 | import { defaultControlsExtra } from './share'; 8 | 9 | const controls = { 10 | workflow: 'test,release', 11 | ...defaultControlsExtra, 12 | }; 13 | 14 | const GithubAction = memo(() => { 15 | const store = useCreateStore(); 16 | 17 | const options = useControls(controls, { store }); 18 | 19 | const md = useMemo(() => genGithubActionsShield(options), [options]); 20 | 21 | return {md.join('\n\n')}; 22 | }); 23 | 24 | export default GithubAction; 25 | -------------------------------------------------------------------------------- /src/ShieldsGithub/Codespace.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 5 | import { GenGithubCodespaceShield } from '@/services/genGithubShield'; 6 | 7 | import { defaultControls } from './share'; 8 | 9 | const controls = defaultControls; 10 | 11 | const GithubCodespace = memo(() => { 12 | const store = useCreateStore(); 13 | 14 | const options = useControls(controls, { store }); 15 | 16 | const md = useMemo(() => { 17 | return GenGithubCodespaceShield(options); 18 | }, [options]); 19 | 20 | return {md.join('\n\n')}; 21 | }); 22 | 23 | export default GithubCodespace; 24 | -------------------------------------------------------------------------------- /src/ShieldsGithub/Contributors.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 5 | import { GenGithubContributorsShield } from '@/services/genGithubShield'; 6 | 7 | import { defaultControls } from './share'; 8 | 9 | const controls = defaultControls; 10 | 11 | const GithubContributors = memo(() => { 12 | const store = useCreateStore(); 13 | 14 | const options = useControls(controls, { store }); 15 | 16 | const md = useMemo(() => { 17 | return GenGithubContributorsShield(options); 18 | }, [options]); 19 | 20 | return {md.join('\n\n')}; 21 | }); 22 | 23 | export default GithubContributors; 24 | -------------------------------------------------------------------------------- /src/ShieldsGithub/Release.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { folder } from 'leva'; 3 | import { memo, useMemo } from 'react'; 4 | 5 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 6 | import { githubShieldControlsPickList } from '@/const/githubShieldControls'; 7 | import { genGithubReleaseShields } from '@/services/genGithubShield'; 8 | 9 | import { defaultControlsExtra } from './share'; 10 | 11 | const controls = defaultControlsExtra; 12 | const pickControls = { ['✅']: folder(githubShieldControlsPickList, { collapsed: true }) }; 13 | 14 | const GithubRelease = memo(() => { 15 | const store = useCreateStore(); 16 | 17 | const options = useControls(controls, { store }); 18 | const pickOptions = useControls(pickControls, { store }); 19 | 20 | const md = useMemo(() => genGithubReleaseShields(options, pickOptions), [options, pickOptions]); 21 | 22 | return {md.join('\n\n')}; 23 | }); 24 | 25 | export default GithubRelease; 26 | -------------------------------------------------------------------------------- /src/ShieldsGithub/Social.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { folder } from 'leva'; 3 | import { memo, useMemo } from 'react'; 4 | 5 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 6 | import { githubSocialControlsPickList } from '@/const/githubShieldControls'; 7 | import { genGithubSocialShields } from '@/services/genGithubShield'; 8 | 9 | import { defaultControlsExtra } from './share'; 10 | 11 | const controls = { 12 | branch: 'main', 13 | ...defaultControlsExtra, 14 | }; 15 | const pickControls = { ['✅']: folder(githubSocialControlsPickList, { collapsed: true }) }; 16 | 17 | const GithubSocial = memo(() => { 18 | const store = useCreateStore(); 19 | 20 | const options = useControls(controls, { store }); 21 | const pickOptions = useControls(pickControls, { store }); 22 | 23 | const md = useMemo(() => genGithubSocialShields(options, pickOptions), [options, pickOptions]); 24 | 25 | return {md.join('\n\n')}; 26 | }); 27 | 28 | export default GithubSocial; 29 | -------------------------------------------------------------------------------- /src/ShieldsGithub/StarHistory.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 5 | import { genGithubStarHistoryShield } from '@/services/genGithubShield'; 6 | 7 | import { defaultControls } from './share'; 8 | 9 | const controls = defaultControls; 10 | 11 | const GithubStarHistory = memo(() => { 12 | const store = useCreateStore(); 13 | 14 | const options = useControls(controls, { store }); 15 | 16 | const md = useMemo(() => { 17 | return genGithubStarHistoryShield(options); 18 | }, [options]); 19 | 20 | return {md}; 21 | }); 22 | 23 | export default GithubStarHistory; 24 | -------------------------------------------------------------------------------- /src/ShieldsGithub/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: shields 4 | title: Github 5 | order: 2 6 | description: Github shields generator 7 | --- 8 | 9 | ## Editor 10 | 11 | ### Social 12 | 13 | 14 | 15 | ### Release 16 | 17 | 18 | 19 | ### Action 20 | 21 | 22 | 23 | ### Star History 24 | 25 | 26 | 27 | ### Codespace 28 | 29 | 30 | 31 | ### Contributors 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/ShieldsGithub/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as ShieldsGithubAction } from './Action'; 2 | export { default as ShieldsGithubCodespace } from './Codespace'; 3 | export { default as ShieldsGithubContributors } from './Contributors'; 4 | export { default as ShieldsGithubRelease } from './Release'; 5 | export { default as ShieldsGithubSocial } from './Social'; 6 | export { default as ShieldsGithubStarHistory } from './StarHistory'; 7 | -------------------------------------------------------------------------------- /src/ShieldsGithub/share.ts: -------------------------------------------------------------------------------- 1 | import { folder } from 'leva'; 2 | import { cloneDeep, pick } from 'lodash-es'; 3 | 4 | import { shieldBaseControls } from '@/const/shieldBaseControls'; 5 | 6 | export const defaultControls = { 7 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 8 | owner: 'lobehub', 9 | repo: 'lobe-chat', 10 | /* eslint-enable */ 11 | }; 12 | 13 | export const defaultControlsExtra = { 14 | ...defaultControls, 15 | ['⚒️']: folder(pick(cloneDeep(shieldBaseControls), ['color', 'labelColor', 'style']), { 16 | collapsed: true, 17 | }), 18 | }; 19 | -------------------------------------------------------------------------------- /src/ShieldsNpm/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: shields 4 | title: NPM 5 | order: 3 6 | description: NPM shields generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ShieldsNpm/index.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { folder } from 'leva'; 3 | import { memo, useMemo } from 'react'; 4 | 5 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 6 | import { npmShieldControlsPickList } from '@/const/npmShieldControls'; 7 | import { genNpmShields } from '@/services/genNpmShield'; 8 | 9 | import { defaultControlsExtra } from './share'; 10 | 11 | const controls = defaultControlsExtra; 12 | const pickControls = { ['✅']: folder(npmShieldControlsPickList, { collapsed: true }) }; 13 | 14 | const Npm = memo(() => { 15 | const store = useCreateStore(); 16 | 17 | const options = useControls(controls, { store }); 18 | const pickOptions = useControls(pickControls, { store }); 19 | 20 | const md = useMemo(() => genNpmShields(options, pickOptions), [options, pickOptions]); 21 | 22 | return {md.join('\n\n')}; 23 | }); 24 | 25 | export default Npm; 26 | -------------------------------------------------------------------------------- /src/ShieldsNpm/share.ts: -------------------------------------------------------------------------------- 1 | import { folder } from 'leva'; 2 | import { cloneDeep, pick } from 'lodash-es'; 3 | 4 | import { shieldBaseControls } from '@/const/shieldBaseControls'; 5 | 6 | export const defaultControls = { 7 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 8 | packageName: '@lobehub/ui', 9 | /* eslint-enable */ 10 | }; 11 | 12 | export const defaultControlsExtra = { 13 | ...defaultControls, 14 | ['⚒️']: folder(pick(cloneDeep(shieldBaseControls), ['color', 'labelColor', 'style']), { 15 | collapsed: true, 16 | }), 17 | }; 18 | -------------------------------------------------------------------------------- /src/ShieldsSocial/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: shields 4 | title: Social 5 | order: 8 6 | description: Social shields generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ShieldsSocial/index.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { folder } from 'leva'; 3 | import { pick } from 'lodash-es'; 4 | import { memo, useMemo } from 'react'; 5 | 6 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 7 | import { shieldBaseControls } from '@/const/shieldBaseControls'; 8 | import { socialShieldControlsPickList } from '@/const/socialShieldControls'; 9 | import { genSocialShields } from '@/services/genSocialShield'; 10 | 11 | const idControls = { 12 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 13 | qq: '40073838', 14 | wechat: '40073838', 15 | x: 'canisminor1990', 16 | weibo: 'Canis_Minor', 17 | discord: 'canisminor1990', 18 | steam: 'canisminor', 19 | /* eslint-enable */ 20 | }; 21 | 22 | const controls = { 23 | prefix: true, 24 | ['⚒️']: folder( 25 | { 26 | ...pick(shieldBaseControls, ['style', 'labelColor', 'color']), 27 | logoColor: { 28 | ...shieldBaseControls.logoColor, 29 | value: 'white', 30 | }, 31 | }, 32 | { 33 | collapsed: true, 34 | }, 35 | ), 36 | }; 37 | const pickControls = { ['✅']: folder(socialShieldControlsPickList, { collapsed: true }) }; 38 | 39 | const Social = memo(() => { 40 | const store = useCreateStore(); 41 | 42 | const idOptions = useControls(idControls, { store }); 43 | const options = useControls(controls, { store }); 44 | const pickOptions = useControls(pickControls, { store }); 45 | 46 | const md = useMemo( 47 | () => genSocialShields(options, idOptions, pickOptions), 48 | [options, idOptions, pickOptions], 49 | ); 50 | 51 | return {md.join('\n\n')}; 52 | }); 53 | 54 | export default Social; 55 | -------------------------------------------------------------------------------- /src/ShieldsVercel/Deploy.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 5 | import { GenVercelDeployShield } from '@/services/genVercelShield'; 6 | 7 | import { defaultControls } from './share'; 8 | 9 | const controls = { 10 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 11 | ...defaultControls, 12 | env: 'OPENAI_API_KEY', 13 | envDescription: 'Find your OpenAI API Key by click the right Learn More button.', 14 | envLink: 'https://platform.openai.com/account/api-keys', 15 | /* eslint-enable */ 16 | }; 17 | 18 | const VercelDeploy = memo(() => { 19 | const store = useCreateStore(); 20 | 21 | const options = useControls(controls, { store }); 22 | 23 | const md = useMemo(() => { 24 | return GenVercelDeployShield(options); 25 | }, [options]); 26 | 27 | return {md.join('\n\n')}; 28 | }); 29 | 30 | export default VercelDeploy; 31 | -------------------------------------------------------------------------------- /src/ShieldsVercel/Website.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 5 | import { genVercelWebsiteShield } from '@/services/genVercelShield'; 6 | 7 | const controls = { 8 | label: '', 9 | url: 'https://lobe-readme-wizard.vercel.app', 10 | }; 11 | 12 | const VercelWebsite = memo(() => { 13 | const store = useCreateStore(); 14 | 15 | const options = useControls(controls, { store }); 16 | 17 | const md = useMemo( 18 | () => 19 | genVercelWebsiteShield({ 20 | label: options.label || options.url.replaceAll('https://', '').replaceAll('http://', ''), 21 | 22 | url: options.url, 23 | }), 24 | [options], 25 | ); 26 | 27 | return {md.join('\n\n')}; 28 | }); 29 | 30 | export default VercelWebsite; 31 | -------------------------------------------------------------------------------- /src/ShieldsVercel/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: shields 4 | title: Vercel 5 | order: 4 6 | description: Vercel shields generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | 13 | 14 | 15 | ### Website 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/ShieldsVercel/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as ShieldsVercelDeploy } from './Deploy'; 2 | export { default as ShieldsVercelWebsite } from './Website'; 3 | -------------------------------------------------------------------------------- /src/ShieldsVercel/share.ts: -------------------------------------------------------------------------------- 1 | export const defaultControls = { 2 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 3 | owner: 'lobehub', 4 | repo: 'lobe-chat', 5 | /* eslint-enable */ 6 | }; 7 | -------------------------------------------------------------------------------- /src/ShieldsWebsite/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: shields 4 | title: Website 5 | order: 1 6 | description: Website shields generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ShieldsWebsite/index.tsx: -------------------------------------------------------------------------------- 1 | import { useControls, useCreateStore } from '@lobehub/ui'; 2 | import { memo, useMemo } from 'react'; 3 | 4 | import { defaultControls } from '@/ShieldsCustom/share'; 5 | import MarkdownStorybook from '@/components/MarkdownStorybook'; 6 | import { shieldBaseControls } from '@/const/shieldBaseControls'; 7 | import { genWebsiteShield } from '@/services/genCustomShield'; 8 | 9 | const controls = { 10 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 11 | label: 'LobeChat', 12 | url: 'https://chat-preview.lobehub.com', 13 | up_message: 'online', 14 | down_message: 'offline', 15 | labelColor: { 16 | ...shieldBaseControls.labelColor, 17 | value: 'black', 18 | }, 19 | ...defaultControls, 20 | /* eslint-enable */ 21 | }; 22 | 23 | const Website = memo(() => { 24 | const store = useCreateStore(); 25 | 26 | const options = useControls(controls, { store }); 27 | 28 | const md = useMemo(() => genWebsiteShield(options), [options]); 29 | 30 | return {md.join('\n\n')}; 31 | }); 32 | 33 | export default Website; 34 | -------------------------------------------------------------------------------- /src/Sponsor/Avatar.tsx: -------------------------------------------------------------------------------- 1 | import type { CSSProperties, FC } from 'react'; 2 | 3 | import { theme } from './style'; 4 | 5 | interface AvatarProps { 6 | name: string; 7 | size?: number; 8 | src?: string; 9 | style?: CSSProperties; 10 | themeMode?: 'light' | 'dark'; 11 | } 12 | 13 | export const Avatar: FC = ({ 14 | src, 15 | name, 16 | size = 64, 17 | style, 18 | 19 | themeMode, 20 | }) => { 21 | const styles = theme(themeMode); 22 | return ( 23 |
38 | {src ? ( 39 | {name} 40 | ) : ( 41 |
48 | {name.slice(0, 2).toUpperCase()} 49 |
50 | )} 51 |
52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /src/Sponsor/const.ts: -------------------------------------------------------------------------------- 1 | import { CSSProperties } from 'react'; 2 | 3 | export const DEFAULT_WIDTH = 800; 4 | export const DEFAULT_AVATAR_SIZE = 64; 5 | 6 | export interface MemberProfile { 7 | MemberId?: number; 8 | company?: string; 9 | createdAt?: string; 10 | currency?: string; 11 | description?: string; 12 | email?: string; 13 | github?: string; 14 | image?: string; 15 | isActive?: boolean; 16 | lastTransactionAmount?: number; 17 | lastTransactionAt?: string; 18 | name: string; 19 | profile?: string; 20 | role?: 'ADMIN' | 'HOST' | 'BACKER'; 21 | tier?: string; 22 | totalAmountDonated: number; 23 | twitter?: string; 24 | type?: 'USER' | 'ORGANIZATION'; 25 | website?: string; 26 | } 27 | 28 | export interface TierItem { 29 | amount: number; 30 | emoji: string; 31 | preset: 'backer' | 'sponsor'; 32 | sort: number; 33 | style?: CSSProperties; 34 | title: string; 35 | } 36 | 37 | export const DEFAULT_GROUP: TierItem[] = [ 38 | { 39 | amount: 250, 40 | emoji: '🥇', 41 | preset: 'sponsor', 42 | sort: 22, 43 | style: { 44 | backgroundImage: `linear-gradient(45deg, #F5E729 0%, #DC9A01 33%, #DC9A01 66%, #F5E729 100%)`, 45 | }, 46 | title: '🥇 Gold Sponsor', 47 | }, 48 | { 49 | amount: 100, 50 | emoji: '🥈', 51 | preset: 'sponsor', 52 | sort: 21, 53 | style: { 54 | backgroundImage: `linear-gradient(45deg, #D8D8D8 0%, #888888 33%, #888888 66%, #D8D8D8 100%)`, 55 | }, 56 | title: '🥈 Silver Sponsor', 57 | }, 58 | { 59 | amount: 50, 60 | emoji: '🥉', 61 | preset: 'sponsor', 62 | sort: 20, 63 | style: { 64 | backgroundImage: `linear-gradient(45deg, #D8974D 0%, #833204 33%, #833204 66%, #D8974D 100%)`, 65 | }, 66 | title: '🥉 Bronze Sponsor', 67 | }, 68 | { 69 | amount: 18, 70 | emoji: '💖', 71 | preset: 'backer', 72 | sort: 11, 73 | title: '💖 Generous Backer', 74 | }, 75 | { 76 | amount: 6, 77 | emoji: '☕', 78 | preset: 'backer', 79 | sort: 10, 80 | title: '☕ Backer', 81 | }, 82 | { 83 | amount: 1, 84 | emoji: '🌟', 85 | preset: 'backer', 86 | sort: 0, 87 | title: '🌟 One Time', 88 | }, 89 | ]; 90 | -------------------------------------------------------------------------------- /src/Sponsor/demos/index.tsx: -------------------------------------------------------------------------------- 1 | import { Sponsor } from '@lobehub/readme-wizard'; 2 | import { StoryBook, useControls, useCreateStore } from '@lobehub/ui'; 3 | import { useThemeMode } from 'antd-style'; 4 | 5 | import { DEFAULT_AVATAR_SIZE, DEFAULT_WIDTH } from '../const'; 6 | import { caleHeight } from '../utils'; 7 | import { data } from './data'; 8 | 9 | export default () => { 10 | const { isDarkMode } = useThemeMode(); 11 | const store = useCreateStore(); 12 | const { width, avatarSize } = useControls( 13 | { 14 | avatarSize: { 15 | step: 1, 16 | value: DEFAULT_AVATAR_SIZE, 17 | }, 18 | 19 | width: { 20 | step: 1, 21 | value: DEFAULT_WIDTH, 22 | }, 23 | }, 24 | { store }, 25 | ); 26 | 27 | return ( 28 | 29 |
36 | 37 |
38 |
39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /src/Sponsor/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: components 3 | group: sponsor 4 | title: SponsorKit 5 | order: 1 6 | description: Sponsor shields generator 7 | --- 8 | 9 | ## Editor 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Sponsor/index.tsx: -------------------------------------------------------------------------------- 1 | import type { CSSProperties, FC } from 'react'; 2 | 3 | import { Sponsorship } from '@/services/sponsorkit/types'; 4 | 5 | import { Avatar } from './Avatar'; 6 | import { DEFAULT_AVATAR_SIZE } from './const'; 7 | import { theme } from './style'; 8 | import { formateSponsorData, getTier } from './utils'; 9 | 10 | export interface SponsorProps { 11 | avatarSize?: number; 12 | data: Sponsorship[]; 13 | height?: number; 14 | padding?: number; 15 | style?: CSSProperties; 16 | texts?: [string, string]; 17 | themeMode?: 'light' | 'dark'; 18 | width?: number; 19 | } 20 | 21 | const Sponsor: FC = ({ 22 | style, 23 | data, 24 | avatarSize = DEFAULT_AVATAR_SIZE, 25 | texts = ['Become ❤️', 'LobeHub'], 26 | themeMode, 27 | }) => { 28 | const styles = theme(themeMode); 29 | 30 | const sponsorData = formateSponsorData(data); 31 | 32 | return ( 33 |
43 | {sponsorData.map((item, index) => { 44 | const tierConfig = getTier(item.tier); 45 | const multiplier = Math.floor(item.totalAmountDonated / tierConfig.amount); 46 | return ( 47 |
58 |
70 | 71 |
72 | {multiplier > 1 && ( 73 |
90 | {`×${Math.floor(multiplier)}`} 91 |
92 | )} 93 |
109 | {tierConfig.emoji} 110 | {item?.name?.length > 8 ? item.name.slice(0, 7) + '...' : item.name} 111 |
112 |
113 | ); 114 | })} 115 | 116 |
131 |
{texts[0]}
132 |
140 | {texts[1]} 141 |
142 | S 143 | p 144 | o 145 | n 146 | s 147 | o 148 | r 149 |
150 |
151 |
152 |
153 | ); 154 | }; 155 | 156 | export default Sponsor; 157 | -------------------------------------------------------------------------------- /src/Sponsor/style.ts: -------------------------------------------------------------------------------- 1 | interface Style { 2 | avatarBackgroundColor: string; 3 | avatarFontColor: string; 4 | backgroundColor: string; 5 | borderColor: string; 6 | fontColor: string; 7 | } 8 | 9 | export const theme = (themeMode?: 'light' | 'dark'): Style => 10 | themeMode === 'dark' 11 | ? { 12 | avatarBackgroundColor: '#111', 13 | avatarFontColor: '#eee', 14 | backgroundColor: '#000', 15 | borderColor: '#333', 16 | fontColor: '#eee', 17 | } 18 | : { 19 | avatarBackgroundColor: '#f5f5f5', 20 | avatarFontColor: '#666', 21 | backgroundColor: '#fff', 22 | borderColor: '#e4e9ec', 23 | fontColor: '#333', 24 | }; 25 | -------------------------------------------------------------------------------- /src/Sponsor/utils.ts: -------------------------------------------------------------------------------- 1 | import { Sponsorship } from '@/services/sponsorkit/types'; 2 | 3 | import { 4 | DEFAULT_AVATAR_SIZE, 5 | DEFAULT_GROUP, 6 | DEFAULT_WIDTH, 7 | MemberProfile, 8 | TierItem, 9 | } from './const'; 10 | 11 | export const caleHeight = ( 12 | data: Sponsorship[] = [], 13 | { 14 | width = DEFAULT_WIDTH, 15 | avatarSize = DEFAULT_AVATAR_SIZE, 16 | }: { avatarSize: number; multiplier?: number; width: number }, 17 | ): number => { 18 | const length = data.length + 2; 19 | const col = width / (avatarSize * 1.4); 20 | return Math.ceil(length / Math.ceil(col)) * avatarSize * 1.4; 21 | }; 22 | 23 | export const formateSponsorData = ( 24 | json: Sponsorship[], 25 | groupBy: TierItem[] = DEFAULT_GROUP, 26 | fallbackTier: string = (DEFAULT_GROUP.at(-1) as TierItem).title, 27 | ): MemberProfile[] => { 28 | const tierSortMap = new Map(groupBy.map((item) => [item.title, item.sort])); 29 | 30 | const getValue = (item: Sponsorship) => 31 | item?.raw?.totalDonations?.value || item?.raw?.amount?.value || item?.monthlyDollars; 32 | 33 | const sortByGroup = (a: Sponsorship, b: Sponsorship) => { 34 | const sortA = tierSortMap.get(a.tierName || fallbackTier) || 0; 35 | const sortB = tierSortMap.get(b.tierName || fallbackTier) || 0; 36 | if (sortA !== sortB) { 37 | return sortB - sortA; 38 | } 39 | 40 | return getValue(b) - getValue(a); 41 | }; 42 | const filteredData = json.filter((item: Sponsorship) => { 43 | const dump = json 44 | .filter((i: Sponsorship) => item.sponsor.login === i.sponsor.login) 45 | .sort(sortByGroup); 46 | if (dump.length > 1 && item.tierName !== dump[0].tierName) return false; 47 | return getValue(item) > 0; 48 | }); 49 | 50 | return filteredData.sort(sortByGroup).map((item) => { 51 | return { 52 | image: item.sponsor.avatarUrl, 53 | name: item.sponsor.name || 'Guest', 54 | tier: item.tierName || fallbackTier, 55 | totalAmountDonated: getValue(item), 56 | }; 57 | }); 58 | }; 59 | 60 | export const getTier = ( 61 | tier: string = (DEFAULT_GROUP.at(-1) as TierItem).title, 62 | groupBy: TierItem[] = DEFAULT_GROUP, 63 | ): TierItem => { 64 | return ( 65 | groupBy.find((item) => item.title.toLowerCase() === tier.toLowerCase()) || 66 | (groupBy.at(-1) as TierItem) 67 | ); 68 | }; 69 | 70 | export const fetchFonts = async () => { 71 | // Regular Font 72 | const fontFileRegular = await fetch( 73 | 'https://gw.alipayobjects.com/os/kitchen/BUfo9kyDYs/HarmonyOS_Sans_Regular.ttf', 74 | { cache: 'force-cache' }, 75 | ); 76 | const fontRegular: ArrayBuffer = await fontFileRegular.arrayBuffer(); 77 | 78 | // Bold Font 79 | const fontFileBold = await fetch( 80 | 'https://gw.alipayobjects.com/os/kitchen/ywwdIaXDZa/HarmonyOS_Sans_Bold.ttf', 81 | { cache: 'force-cache' }, 82 | ); 83 | const fontBold: ArrayBuffer = await fontFileBold.arrayBuffer(); 84 | 85 | return { fontBold, fontRegular }; 86 | }; 87 | 88 | export const getNumber = (value: string | null, defaultValue?: number) => { 89 | if (!value || value === null) return defaultValue; 90 | const parsed = Number.parseInt(value, 10); 91 | if (Number.isNaN(parsed)) return defaultValue; 92 | return parsed; 93 | }; 94 | -------------------------------------------------------------------------------- /src/components/Highlight/index.tsx: -------------------------------------------------------------------------------- 1 | import { Highlighter, HighlighterProps } from '@lobehub/ui'; 2 | import { memo } from 'react'; 3 | import useSWR from 'swr'; 4 | 5 | import { remarkFormat } from '@/utils/remarkFormat'; 6 | 7 | const Highlight = memo(({ children, ...props }) => { 8 | const { data, isLoading } = useSWR(children, () => remarkFormat(children)); 9 | 10 | return ( 11 | 12 | {isLoading ? '' : String(data)} 13 | 14 | ); 15 | }); 16 | 17 | export default Highlight; 18 | -------------------------------------------------------------------------------- /src/components/Label/index.tsx: -------------------------------------------------------------------------------- 1 | import { LucideIcon } from 'lucide-react'; 2 | import { memo } from 'react'; 3 | import { Flexbox } from 'react-layout-kit'; 4 | 5 | const Label = memo<{ icon?: LucideIcon; title: string }>(({ title, icon }) => { 6 | if (!icon) return title; 7 | const Render = icon; 8 | return ( 9 | 10 | 11 | {title} 12 | 13 | ); 14 | }); 15 | 16 | export default Label; 17 | -------------------------------------------------------------------------------- /src/components/Markdown/HighlightStyle.tsx: -------------------------------------------------------------------------------- 1 | import { FontLoader, genCdnUrl } from '@lobehub/ui'; 2 | import { useThemeMode } from 'antd-style'; 3 | import { memo } from 'react'; 4 | 5 | const HighlightStyle = memo(() => { 6 | const { isDarkMode } = useThemeMode(); 7 | 8 | return ( 9 | 16 | ); 17 | }); 18 | 19 | export default HighlightStyle; 20 | -------------------------------------------------------------------------------- /src/components/Markdown/index.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from 'antd'; 2 | import { CSSProperties, memo } from 'react'; 3 | import ReactMarkdown from 'react-markdown'; 4 | import rehypeHighlight from 'rehype-highlight'; 5 | import rehypeRaw from 'rehype-raw'; 6 | import remarkGfm from 'remark-gfm'; 7 | import remarkToc from 'remark-toc'; 8 | 9 | import HighlightStyle from './HighlightStyle'; 10 | import { useStyles } from './style'; 11 | 12 | export interface MarkdownProps { 13 | children: string; 14 | className?: string; 15 | style?: CSSProperties; 16 | } 17 | 18 | const Markdown = memo(({ children, className, style, ...props }) => { 19 | const { styles, cx } = useStyles(); 20 | const rehypePlugins = [rehypeRaw, [rehypeHighlight, { ignoreMissing: true }]] as any; 21 | const remarkPlugins = [remarkGfm, remarkToc]; 22 | 23 | return ( 24 |
25 | 26 | 27 | 33 | {children} 34 | 35 | 36 |
37 | ); 38 | }); 39 | 40 | export default Markdown; 41 | -------------------------------------------------------------------------------- /src/components/Markdown/style.ts: -------------------------------------------------------------------------------- 1 | import { createStyles } from 'antd-style'; 2 | 3 | export const useStyles = createStyles(({ css, isDarkMode, cx }) => { 4 | const scheme = isDarkMode 5 | ? css` 6 | --color-prettylights-syntax-comment: #8b949e; 7 | --color-prettylights-syntax-constant: #79c0ff; 8 | --color-prettylights-syntax-entity: #d2a8ff; 9 | --color-prettylights-syntax-storage-modifier-import: #c9d1d9; 10 | --color-prettylights-syntax-entity-tag: #7ee787; 11 | --color-prettylights-syntax-keyword: #ff7b72; 12 | --color-prettylights-syntax-string: #a5d6ff; 13 | --color-prettylights-syntax-variable: #ffa657; 14 | --color-prettylights-syntax-brackethighlighter-unmatched: #f85149; 15 | --color-prettylights-syntax-invalid-illegal-text: #f0f6fc; 16 | --color-prettylights-syntax-invalid-illegal-bg: #8e1519; 17 | --color-prettylights-syntax-carriage-return-text: #f0f6fc; 18 | --color-prettylights-syntax-carriage-return-bg: #b62324; 19 | --color-prettylights-syntax-string-regexp: #7ee787; 20 | --color-prettylights-syntax-markup-list: #f2cc60; 21 | --color-prettylights-syntax-markup-heading: #1f6feb; 22 | --color-prettylights-syntax-markup-italic: #c9d1d9; 23 | --color-prettylights-syntax-markup-bold: #c9d1d9; 24 | --color-prettylights-syntax-markup-deleted-text: #ffdcd7; 25 | --color-prettylights-syntax-markup-deleted-bg: #67060c; 26 | --color-prettylights-syntax-markup-inserted-text: #aff5b4; 27 | --color-prettylights-syntax-markup-inserted-bg: #033a16; 28 | --color-prettylights-syntax-markup-changed-text: #ffdfb6; 29 | --color-prettylights-syntax-markup-changed-bg: #5a1e02; 30 | --color-prettylights-syntax-markup-ignored-text: #c9d1d9; 31 | --color-prettylights-syntax-markup-ignored-bg: #1158c7; 32 | --color-prettylights-syntax-meta-diff-range: #d2a8ff; 33 | --color-prettylights-syntax-brackethighlighter-angle: #8b949e; 34 | --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58; 35 | --color-prettylights-syntax-constant-other-reference-link: #a5d6ff; 36 | --color-fg-default: #c9d1d9; 37 | --color-fg-muted: #8b949e; 38 | --color-fg-subtle: #6e7681; 39 | --color-canvas-default: #0d1117; 40 | --color-canvas-subtle: #161b22; 41 | --color-border-default: #30363d; 42 | --color-border-muted: #21262d; 43 | --color-neutral-muted: rgba(110, 118, 129, 40%); 44 | --color-accent-fg: #58a6ff; 45 | --color-accent-emphasis: #1f6feb; 46 | --color-attention-subtle: rgba(187, 128, 9, 15%); 47 | --color-danger-fg: #f85149; 48 | 49 | color-scheme: dark; 50 | ` 51 | : css` 52 | --color-prettylights-syntax-comment: #6e7781; 53 | --color-prettylights-syntax-constant: #0550ae; 54 | --color-prettylights-syntax-entity: #8250df; 55 | --color-prettylights-syntax-storage-modifier-import: #24292f; 56 | --color-prettylights-syntax-entity-tag: #116329; 57 | --color-prettylights-syntax-keyword: #cf222e; 58 | --color-prettylights-syntax-string: #0a3069; 59 | --color-prettylights-syntax-variable: #953800; 60 | --color-prettylights-syntax-brackethighlighter-unmatched: #82071e; 61 | --color-prettylights-syntax-invalid-illegal-text: #f6f8fa; 62 | --color-prettylights-syntax-invalid-illegal-bg: #82071e; 63 | --color-prettylights-syntax-carriage-return-text: #f6f8fa; 64 | --color-prettylights-syntax-carriage-return-bg: #cf222e; 65 | --color-prettylights-syntax-string-regexp: #116329; 66 | --color-prettylights-syntax-markup-list: #3b2300; 67 | --color-prettylights-syntax-markup-heading: #0550ae; 68 | --color-prettylights-syntax-markup-italic: #24292f; 69 | --color-prettylights-syntax-markup-bold: #24292f; 70 | --color-prettylights-syntax-markup-deleted-text: #82071e; 71 | --color-prettylights-syntax-markup-deleted-bg: #ffebe9; 72 | --color-prettylights-syntax-markup-inserted-text: #116329; 73 | --color-prettylights-syntax-markup-inserted-bg: #dafbe1; 74 | --color-prettylights-syntax-markup-changed-text: #953800; 75 | --color-prettylights-syntax-markup-changed-bg: #ffd8b5; 76 | --color-prettylights-syntax-markup-ignored-text: #eaeef2; 77 | --color-prettylights-syntax-markup-ignored-bg: #0550ae; 78 | --color-prettylights-syntax-meta-diff-range: #8250df; 79 | --color-prettylights-syntax-brackethighlighter-angle: #57606a; 80 | --color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f; 81 | --color-prettylights-syntax-constant-other-reference-link: #0a3069; 82 | --color-fg-default: #24292f; 83 | --color-fg-muted: #57606a; 84 | --color-fg-subtle: #6e7781; 85 | --color-canvas-default: #fff; 86 | --color-canvas-subtle: #f6f8fa; 87 | --color-border-default: #d0d7de; 88 | --color-border-muted: hsla(210deg, 18%, 87%, 100%); 89 | --color-neutral-muted: rgba(175, 184, 193, 20%); 90 | --color-accent-fg: #0969da; 91 | --color-accent-emphasis: #0969da; 92 | --color-attention-subtle: #fff8c5; 93 | --color-danger-fg: #cf222e; 94 | 95 | color-scheme: light; 96 | `; 97 | return { 98 | container: css` 99 | position: relative; 100 | overflow: hidden; 101 | `, 102 | markdown: cx( 103 | 'markdown-body', 104 | css` 105 | position: relative; 106 | height: 100%; 107 | padding: 24px; 108 | 109 | [align='center'] { 110 | text-align: center !important; 111 | 112 | p { 113 | text-align: center !important; 114 | } 115 | } 116 | 117 | [align='right'] { 118 | text-align: right !important; 119 | 120 | p { 121 | text-align: right !important; 122 | } 123 | } 124 | `, 125 | ), 126 | scheme, 127 | }; 128 | }); 129 | -------------------------------------------------------------------------------- /src/components/MarkdownEditor/index.tsx: -------------------------------------------------------------------------------- 1 | import { CodeEditor } from '@lobehub/ui'; 2 | import { Segmented } from 'antd'; 3 | import { memo, useState } from 'react'; 4 | import { Flexbox } from 'react-layout-kit'; 5 | import useControlledState from 'use-merge-value'; 6 | 7 | import Markdown from '@/components/Markdown'; 8 | 9 | import { useStyles } from './style'; 10 | 11 | enum Tabs { 12 | Editor = 'editor', 13 | Preview = 'preview', 14 | Split = 'split', 15 | } 16 | 17 | interface MarkdownEditorProps { 18 | onChange: (text: string) => void; 19 | value: string; 20 | } 21 | 22 | const MarkdownEditor = memo(({ onChange, value }) => { 23 | const [currentValue, setCurrentValue] = useControlledState(value, { 24 | defaultValue: value, 25 | onChange, 26 | value, 27 | }); 28 | const [tab, setTab] = useState(Tabs.Editor); 29 | const { styles } = useStyles(); 30 | 31 | const editor = ( 32 | 39 | ); 40 | 41 | const preview = {currentValue}; 42 | 43 | return ( 44 | 45 | 64 | {tab === 'split' ? ( 65 | 66 | {preview} 67 | {editor} 68 | 69 | ) : ( 70 |
71 | {tab === Tabs.Preview && preview} 72 | {tab === Tabs.Editor && editor} 73 |
74 | )} 75 |
76 | ); 77 | }); 78 | 79 | export default MarkdownEditor; 80 | -------------------------------------------------------------------------------- /src/components/MarkdownEditor/style.ts: -------------------------------------------------------------------------------- 1 | import { createStyles } from 'antd-style'; 2 | 3 | export const useStyles = createStyles(({ css, token, responsive }) => { 4 | return { 5 | container: css` 6 | position: relative; 7 | 8 | overflow: hidden; 9 | 10 | width: 100%; 11 | 12 | background: ${token.colorBgContainer}; 13 | border-radius: ${token.borderRadiusLG}px; 14 | box-shadow: 0 0 0 1px ${token.colorBorder}; 15 | `, 16 | editor: css` 17 | flex: 1; 18 | padding: 16px; 19 | `, 20 | markdown: css` 21 | flex: 1; 22 | border-right: 1px solid ${token.colorBorder}; 23 | 24 | ${responsive.mobile} { 25 | border-left: none; 26 | } 27 | `, 28 | }; 29 | }); 30 | -------------------------------------------------------------------------------- /src/components/MarkdownPreivew/index.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import { Flexbox } from 'react-layout-kit'; 3 | 4 | import Highlight from '@/components/Highlight'; 5 | import Markdown from '@/components/Markdown'; 6 | 7 | import { useStyles } from './style'; 8 | 9 | const MarkdownEditor = memo<{ children: string }>(({ children }) => { 10 | const { styles } = useStyles(); 11 | 12 | return ( 13 | 14 | {children}; 15 | 16 | {children} 17 | 18 | 19 | ); 20 | }); 21 | 22 | export default MarkdownEditor; 23 | -------------------------------------------------------------------------------- /src/components/MarkdownPreivew/style.ts: -------------------------------------------------------------------------------- 1 | import { createStyles } from 'antd-style'; 2 | 3 | export const useStyles = createStyles(({ css, token }) => { 4 | return { 5 | container: css` 6 | position: relative; 7 | overflow: hidden; 8 | width: 100%; 9 | height: 100%; 10 | `, 11 | markdown: css` 12 | overflow: hidden; 13 | flex: 1; 14 | height: 100%; 15 | border-bottom: 1px solid ${token.colorBorder}; 16 | `, 17 | preview: css` 18 | flex: 1; 19 | border-radius: 0; 20 | `, 21 | }; 22 | }); 23 | -------------------------------------------------------------------------------- /src/components/MarkdownStorybook/index.tsx: -------------------------------------------------------------------------------- 1 | import { StoryBook, StoryBookProps } from '@lobehub/ui'; 2 | import { memo } from 'react'; 3 | import { Flexbox } from 'react-layout-kit'; 4 | 5 | import Highlight from '@/components/Highlight'; 6 | import Markdown from '@/components/Markdown'; 7 | 8 | import { useStyles } from './style'; 9 | 10 | const MarkdownEditor = memo<{ children: string; levaStore: StoryBookProps['levaStore'] }>( 11 | ({ children, levaStore }) => { 12 | const { styles } = useStyles(); 13 | 14 | return ( 15 | 16 | 17 | {children} 18 | 19 | 20 | {children} 21 | 22 | 23 | ); 24 | }, 25 | ); 26 | 27 | export default MarkdownEditor; 28 | -------------------------------------------------------------------------------- /src/components/MarkdownStorybook/style.ts: -------------------------------------------------------------------------------- 1 | import { createStyles } from 'antd-style'; 2 | 3 | export const useStyles = createStyles(({ css, token }) => { 4 | return { 5 | container: css` 6 | position: relative; 7 | 8 | overflow: hidden; 9 | 10 | width: 100%; 11 | margin: 16px 0 32px; 12 | 13 | border-radius: ${token.borderRadiusLG}px; 14 | box-shadow: 0 0 0 1px ${token.colorBorder}; 15 | `, 16 | markdown: css` 17 | overflow-x: hidden; 18 | overflow-y: auto; 19 | 20 | width: 100%; 21 | height: 100%; 22 | 23 | background: ${token.colorBgContainer}; 24 | `, 25 | preview: css` 26 | border-top: 1px solid ${token.colorBorder}; 27 | border-radius: 0; 28 | `, 29 | }; 30 | }); 31 | -------------------------------------------------------------------------------- /src/components/Title/index.tsx: -------------------------------------------------------------------------------- 1 | import { ActionIcon } from '@lobehub/ui'; 2 | import { kebabCase } from 'lodash-es'; 3 | import { Link } from 'lucide-react'; 4 | import { memo } from 'react'; 5 | import { Flexbox } from 'react-layout-kit'; 6 | 7 | export interface TitleProps { 8 | children: string; 9 | link?: string; 10 | } 11 | 12 | const Title = memo(({ children, link }) => { 13 | const titleContent =

{children}

; 14 | if (!link) return titleContent; 15 | return ( 16 | 17 | {titleContent} 18 | 19 | 20 | 21 | 22 | ); 23 | }); 24 | 25 | export default Title; 26 | -------------------------------------------------------------------------------- /src/const/dockerShieldControls.ts: -------------------------------------------------------------------------------- 1 | import urlJoin from 'url-join'; 2 | 3 | import { colorOptions } from '@/const/shieldBaseControls'; 4 | import { DOCKER_URL, SHIELD_DOCKER_URL } from '@/const/url'; 5 | import { ShieldsBaseOptions } from '@/types/shields'; 6 | import { genPickList } from '@/utils/genPickList'; 7 | 8 | export interface DockerShieldControlItem extends Partial { 9 | genLink?: (packageName: string) => string | undefined; 10 | suffix?: string; 11 | url: string; 12 | } 13 | 14 | const genLink: DockerShieldControlItem['genLink'] = (packageName) => 15 | urlJoin(DOCKER_URL, packageName); 16 | 17 | export const dockerShieldControls: { 18 | [key: string]: DockerShieldControlItem; 19 | } = { 20 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 21 | release: { 22 | logo: 'docker', 23 | logoColor: 'white', 24 | label: 'docker', 25 | color: colorOptions.geekblue, 26 | genLink, 27 | url: urlJoin(SHIELD_DOCKER_URL, 'v'), 28 | }, 29 | size: { 30 | genLink, 31 | color: colorOptions.geekblue, 32 | url: urlJoin(SHIELD_DOCKER_URL, 'image-size'), 33 | }, 34 | pulls: { 35 | genLink, 36 | color: '45cc11', 37 | url: urlJoin(SHIELD_DOCKER_URL, 'pulls'), 38 | }, 39 | /* eslint-enable */ 40 | }; 41 | 42 | export const dockerShieldControlsPickList = genPickList(dockerShieldControls); 43 | -------------------------------------------------------------------------------- /src/const/githubShieldControls.ts: -------------------------------------------------------------------------------- 1 | import urlJoin from 'url-join'; 2 | 3 | import { colorOptions } from '@/const/shieldBaseControls'; 4 | import { GITHUB_URL, SHIELD_GITHUB_URL } from '@/const/url'; 5 | import { GithubShieldBaseOptions, ShieldsBaseOptions } from '@/types/shields'; 6 | import { genPickList } from '@/utils/genPickList'; 7 | 8 | export interface GithubShieldControlItem extends Partial { 9 | genLink?: (options: GithubShieldBaseOptions) => string | undefined; 10 | suffix?: string; 11 | url: string; 12 | } 13 | 14 | export const githubSocialControls: { 15 | [key: string]: GithubShieldControlItem; 16 | } = { 17 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 18 | contributors: { 19 | color: colorOptions.lime, 20 | genLink: ({ owner, repo }) => urlJoin(GITHUB_URL, owner, repo, 'graphs/contributors'), 21 | url: urlJoin(SHIELD_GITHUB_URL, 'contributors'), 22 | }, 23 | forks: { 24 | color: colorOptions.blue, 25 | genLink: ({ owner, repo }) => urlJoin(GITHUB_URL, owner, repo, 'network/members'), 26 | url: urlJoin(SHIELD_GITHUB_URL, 'forks'), 27 | }, 28 | stars: { 29 | color: colorOptions.gold, 30 | genLink: ({ owner, repo }) => urlJoin(GITHUB_URL, owner, repo, 'network/stargazers'), 31 | url: urlJoin(SHIELD_GITHUB_URL, 'stars'), 32 | }, 33 | issues: { 34 | color: colorOptions.magenta, 35 | genLink: ({ owner, repo }) => urlJoin(GITHUB_URL, owner, repo, 'issues'), 36 | url: urlJoin(SHIELD_GITHUB_URL, 'issues'), 37 | }, 38 | license: { 39 | color: colorOptions.white, 40 | genLink: ({ owner, repo, branch }) => 41 | branch && urlJoin(GITHUB_URL, owner, repo, 'blob', branch, 'LICENSE'), 42 | url: urlJoin(SHIELD_GITHUB_URL, 'license'), 43 | }, 44 | /* eslint-enable */ 45 | }; 46 | 47 | export const githubSocialControlsPickList = genPickList(githubSocialControls); 48 | 49 | export const githubShieldControls: { 50 | [key: string]: GithubShieldControlItem; 51 | } = { 52 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 53 | release: { 54 | logo: 'github', 55 | color: colorOptions.geekblue, 56 | genLink: ({ owner, repo }) => urlJoin(GITHUB_URL, owner, repo, 'releases'), 57 | url: urlJoin(SHIELD_GITHUB_URL, 'v/release'), 58 | }, 59 | releaseDate: { 60 | genLink: ({ owner, repo }) => urlJoin(GITHUB_URL, owner, repo, 'releases'), 61 | url: urlJoin(SHIELD_GITHUB_URL, 'release-date'), 62 | }, 63 | downloads: { 64 | genLink: ({ owner, repo }) => urlJoin(GITHUB_URL, owner, repo, 'releases'), 65 | suffix: 'total', 66 | url: urlJoin(SHIELD_GITHUB_URL, 'downloads'), 67 | }, 68 | /* eslint-enable */ 69 | }; 70 | 71 | export const githubShieldControlsPickList = genPickList(githubShieldControls); 72 | -------------------------------------------------------------------------------- /src/const/npmShieldControls.ts: -------------------------------------------------------------------------------- 1 | import urlJoin from 'url-join'; 2 | 3 | import { colorOptions } from '@/const/shieldBaseControls'; 4 | import { NPM_URL, SHIELD_NPM_URL } from '@/const/url'; 5 | import { ShieldsBaseOptions } from '@/types/shields'; 6 | import { genPickList } from '@/utils/genPickList'; 7 | 8 | export interface NpmShieldControlItem extends Partial { 9 | genLink?: (packageName: string) => string | undefined; 10 | suffix?: string; 11 | url: string; 12 | } 13 | 14 | const genLink: NpmShieldControlItem['genLink'] = (packageName) => urlJoin(NPM_URL, packageName); 15 | 16 | export const npmShieldControls: { 17 | [key: string]: NpmShieldControlItem; 18 | } = { 19 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 20 | release: { 21 | logo: 'npm', 22 | logoColor: 'white', 23 | color: colorOptions.geekblue, 24 | genLink, 25 | url: urlJoin(SHIELD_NPM_URL, 'v'), 26 | }, 27 | downloads: { 28 | genLink, 29 | url: urlJoin(SHIELD_NPM_URL, 'dt'), 30 | }, 31 | types: { 32 | genLink, 33 | url: urlJoin(SHIELD_NPM_URL, 'types'), 34 | }, 35 | /* eslint-enable */ 36 | }; 37 | 38 | export const npmShieldControlsPickList = genPickList(npmShieldControls); 39 | -------------------------------------------------------------------------------- /src/const/sample.ts: -------------------------------------------------------------------------------- 1 | import urlJoin from 'url-join'; 2 | 3 | import { GITHUB_URL, SHIELD_BADGE_URL } from '@/const/url'; 4 | import { genShield } from '@/utils/genShield'; 5 | 6 | export const featuresSample = `- [x] 💨 **Quick Deployment**: Using the Vercel platform, you can deploy with just one click and complete the process within 1 minute, without any complex configuration; 7 | - [x] 💎 **Exquisite UI Design**: With a carefully designed interface, it offers an elegant appearance and smooth interaction. It supports light and dark themes and is mobile-friendly. PWA support provides a more native-like experience; 8 | - [x] 🗣️ **Smooth Conversation Experience**: Fluid responses ensure a smooth conversation experience. It fully supports Markdown rendering, including code highlighting, LaTex formulas, Mermaid flowcharts, and more; 9 | `; 10 | 11 | export const creditsSample = `### More Products 12 | 13 | - **[🤖 Lobe Chat](https://github.com/lobehub/lobe-chat)** - An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application. 14 | - **[🤯 Lobe theme](https://github.com/lobehub/sd-webui-lobe-theme)** - The modern theme for stable diffusion webui, exquisite interface design, highly customizable UI, and efficiency boosting features. 15 | 16 | ### Credits 17 | 18 | - **remark** - https://github.com/remarkjs/remark 19 | - **shikiji** - https://github.com/antfu/shikiji 20 | `; 21 | 22 | export const bunShields = genShield( 23 | 'bun', 24 | urlJoin(SHIELD_BADGE_URL, '-speedup%20with%20bun-black?logo=bun&style=for-the-badge'), 25 | 'https://bun.sh', 26 | ); 27 | 28 | export const prWelcomeShields = (prWelcome: string, owner: string, repo: string) => 29 | genShield( 30 | 'pr-welcome', 31 | urlJoin( 32 | SHIELD_BADGE_URL, 33 | `${encodeURIComponent(prWelcome)}-%E2%86%92-ffcb47?labelColor=black&style=for-the-badge`, 34 | ), 35 | urlJoin(GITHUB_URL, owner, repo, 'pulls'), 36 | ); 37 | -------------------------------------------------------------------------------- /src/const/shareShieldControls.ts: -------------------------------------------------------------------------------- 1 | import { identity, pickBy } from 'lodash-es'; 2 | import qs from 'query-string'; 3 | 4 | import { ShieldsBaseOptions } from '@/types/shields'; 5 | import { genPickList } from '@/utils/genPickList'; 6 | 7 | export interface shareShieldControlsItem extends Partial { 8 | genLink?: (props: { 9 | desc?: string; 10 | hashtags?: string; 11 | title?: string; 12 | url?: string; 13 | }) => string | undefined; 14 | } 15 | 16 | const formateHashtags = (hashtags: string): string[] => 17 | hashtags.replaceAll(',', ',').replaceAll(' ', '').split(','); 18 | 19 | const stringifyHashtags = (hashtags: string, joinfix: string = ',', prefix?: string) => { 20 | let tags = formateHashtags(hashtags.trim()); 21 | if (prefix) tags = tags.map((tag) => prefix + tag); 22 | return tags.filter(Boolean).join(joinfix); 23 | }; 24 | 25 | export const shareShieldControls: { 26 | [key: string]: shareShieldControlsItem; 27 | } = { 28 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 29 | x: { 30 | logo: 'x', 31 | logoColor: 'white', 32 | genLink: ({ url, title, desc, hashtags }) => { 33 | const query = pickBy( 34 | { 35 | text: [title, desc].filter(Boolean).join(' - '), 36 | url, 37 | hashtags: hashtags && stringifyHashtags(hashtags), 38 | }, 39 | identity, 40 | ) as any; 41 | return qs.stringifyUrl({ 42 | url: 'https://x.com/intent/tweet', 43 | query, 44 | }); 45 | }, 46 | }, 47 | telegram: { 48 | logo: 'telegram', 49 | logoColor: 'white', 50 | genLink: ({ url, title, desc, hashtags }) => { 51 | const query = pickBy( 52 | { 53 | text: [ 54 | [title, desc].filter(Boolean).join(' - '), 55 | hashtags && stringifyHashtags(hashtags, ' ', '#'), 56 | ] 57 | .filter(Boolean) 58 | .join(' '), 59 | url, 60 | }, 61 | identity, 62 | ) as any; 63 | return qs.stringifyUrl({ 64 | url: 'https://t.me/share/url"', 65 | query, 66 | }); 67 | }, 68 | }, 69 | whatsapp: { 70 | logo: 'whatsapp', 71 | logoColor: 'white', 72 | genLink: ({ url, title, desc, hashtags }) => { 73 | const query = pickBy( 74 | { 75 | text: [ 76 | [title, desc].filter(Boolean).join(' - '), 77 | url, 78 | hashtags && stringifyHashtags(hashtags, ' ', '#'), 79 | ] 80 | .filter(Boolean) 81 | .join(' '), 82 | }, 83 | identity, 84 | ) as any; 85 | return qs.stringifyUrl({ 86 | url: 'https://api.whatsapp.com/send', 87 | query, 88 | }); 89 | }, 90 | }, 91 | reddit: { 92 | logo: 'reddit', 93 | logoColor: 'white', 94 | genLink: ({ url, title, desc, hashtags }) => { 95 | const query = pickBy( 96 | { 97 | title: [ 98 | [title, desc].filter(Boolean).join(' - '), 99 | hashtags && stringifyHashtags(hashtags, ' ', '#'), 100 | ] 101 | .filter(Boolean) 102 | .join(' '), 103 | url, 104 | }, 105 | identity, 106 | ) as any; 107 | return qs.stringifyUrl({ 108 | url: 'https://www.reddit.com/submit', 109 | query, 110 | }); 111 | }, 112 | }, 113 | weibo: { 114 | logo: 'sinaweibo', 115 | logoColor: 'white', 116 | genLink: ({ url, title, desc, hashtags }) => { 117 | const query = pickBy( 118 | { 119 | sharesource: 'weibo', 120 | title: [ 121 | [title, desc].filter(Boolean).join(' - '), 122 | hashtags && stringifyHashtags(hashtags, ' ', '#'), 123 | ] 124 | .filter(Boolean) 125 | .join(' '), 126 | url, 127 | }, 128 | identity, 129 | ) as any; 130 | return qs.stringifyUrl({ 131 | url: 'http://service.weibo.com/share/share.php', 132 | query, 133 | }); 134 | }, 135 | }, 136 | qq: { 137 | logo: 'tencentqq', 138 | logoColor: 'white', 139 | genLink: ({ url, title, desc, hashtags }) => { 140 | const query = pickBy( 141 | { 142 | title, 143 | desc: [desc, hashtags && stringifyHashtags(hashtags, ' ', '#')].filter(Boolean).join(' '), 144 | summary: [title, desc].filter(Boolean).join(' - '), 145 | url, 146 | sharesource: 'qzone', 147 | }, 148 | identity, 149 | ) as any; 150 | return qs.stringifyUrl({ 151 | url: 'http://connect.qq.com/widget/shareqq/index.html', 152 | query, 153 | }); 154 | }, 155 | }, 156 | /* eslint-enable */ 157 | }; 158 | 159 | export const shareShieldControlsPickList = genPickList(shareShieldControls); 160 | -------------------------------------------------------------------------------- /src/const/shieldBaseControls.ts: -------------------------------------------------------------------------------- 1 | import icons from './icons'; 2 | 3 | export const colorOptions = { 4 | black: 'black', 5 | blue: '8ae8ff', 6 | cyan: '95f3d9', 7 | geekblue: '369eff', 8 | gold: 'ffcb47', 9 | green: '55b467', 10 | lime: 'c4f042', 11 | magenta: 'ff80eb', 12 | orange: 'ff802b', 13 | purple: 'B0A3FF', 14 | red: 'f04f88', 15 | volcano: 'ec5e41', 16 | white: 'white', 17 | yellow: 'ffef5c', 18 | } as const; 19 | 20 | export const shieldBaseControls = { 21 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 22 | label: '', 23 | color: { 24 | options: colorOptions, 25 | value: '', 26 | }, 27 | labelColor: { 28 | options: colorOptions, 29 | value: 'black', 30 | }, 31 | logo: { 32 | options: icons, 33 | value: '', 34 | }, 35 | logoColor: { 36 | options: colorOptions, 37 | value: '', 38 | }, 39 | style: { 40 | options: ['flat', 'flat-square', 'plastic', 'for-the-badge', 'social'], 41 | value: 'flat-square', 42 | }, 43 | /* eslint-enable */ 44 | } as const; 45 | -------------------------------------------------------------------------------- /src/const/socialShieldControls.ts: -------------------------------------------------------------------------------- 1 | import urlJoin from 'url-join'; 2 | 3 | import { colorOptions } from '@/const/shieldBaseControls'; 4 | import { ShieldsBaseOptions } from '@/types/shields'; 5 | import { genPickList } from '@/utils/genPickList'; 6 | 7 | export interface socialShieldControlsItem extends Partial { 8 | genLink?: (id: string) => string | undefined; 9 | } 10 | 11 | export const socialShieldControls: { 12 | [key: string]: socialShieldControlsItem; 13 | } = { 14 | /* eslint-disable sort-keys-fix/sort-keys-fix */ 15 | qq: { 16 | logo: 'tencentqq', 17 | logoColor: 'white', 18 | color: colorOptions.blue, 19 | }, 20 | wechat: { 21 | logo: 'wechat', 22 | logoColor: 'white', 23 | color: colorOptions.lime, 24 | }, 25 | discord: { 26 | logo: 'discord', 27 | logoColor: 'white', 28 | color: colorOptions.purple, 29 | }, 30 | weibo: { 31 | logo: 'sinaweibo', 32 | logoColor: 'white', 33 | color: 'FF9F9F', 34 | genLink: (id) => urlJoin('https://weibo.com/n', id), 35 | }, 36 | steam: { 37 | logo: 'steam', 38 | logoColor: 'white', 39 | color: 'ABCAFF', 40 | genLink: (id) => urlJoin('https://steamcommunity.com/id', id), 41 | }, 42 | x: { 43 | logo: 'x', 44 | logoColor: 'white', 45 | color: colorOptions.white, 46 | genLink: (id) => urlJoin('https://x.com', id), 47 | }, 48 | /* eslint-enable */ 49 | }; 50 | 51 | export const socialShieldControlsPickList = genPickList(socialShieldControls); 52 | -------------------------------------------------------------------------------- /src/const/url.ts: -------------------------------------------------------------------------------- 1 | import urlJoin from 'url-join'; 2 | 3 | export const GITHUB_URL = 'https://github.com'; 4 | export const GITHUB_STAR_HISTORY_URL = 'https://api.star-history.com/svg'; 5 | export const GITHUBE_CONTRIB_URL = 'https://contrib.rocks/image'; 6 | export const NPM_URL = 'https://www.npmjs.com/package'; 7 | export const DOCKER_URL = 'https://hub.docker.com/r'; 8 | export const SHIELD_URL = 'https://img.shields.io'; 9 | export const SHIELD_BADGE_URL = urlJoin(SHIELD_URL, 'badge'); 10 | export const SHIELD_GITHUB_URL = urlJoin(SHIELD_URL, 'github'); 11 | export const SHIELD_NPM_URL = urlJoin(SHIELD_URL, 'npm'); 12 | export const SHIELD_DOCKER_URL = urlJoin(SHIELD_URL, 'docker'); 13 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { genSponsor } from './services/genSponsor'; 2 | export { default as Sponsor, type SponsorProps } from './Sponsor'; 3 | -------------------------------------------------------------------------------- /src/services/genCustomShield.ts: -------------------------------------------------------------------------------- 1 | import { identity, pickBy } from 'lodash-es'; 2 | import qs from 'query-string'; 3 | import urlJoin from 'url-join'; 4 | 5 | import { SHIELD_BADGE_URL, SHIELD_URL } from '@/const/url'; 6 | import { ShieldsBaseOptions } from '@/types/shields'; 7 | import { formatCustomLabel } from '@/utils/formatCustomLabel'; 8 | import { genShield } from '@/utils/genShield'; 9 | 10 | interface CustomSingleShieldOptions extends ShieldsBaseOptions { 11 | label: string; 12 | } 13 | 14 | export const genCustomSingleShield = (options: CustomSingleShieldOptions) => { 15 | const { link, label, color, ...config } = options; 16 | const url = qs.stringifyUrl({ 17 | query: pickBy(config, identity) as any, 18 | url: urlJoin( 19 | SHIELD_BADGE_URL, 20 | formatCustomLabel({ 21 | color: color as string, 22 | label, 23 | }), 24 | ), 25 | }); 26 | 27 | return genShield(label, url, link); 28 | }; 29 | 30 | interface CustomDoubleShieldOptions extends ShieldsBaseOptions { 31 | content: string; 32 | label: string; 33 | } 34 | 35 | export const genCustomDoubleShield = (options: CustomDoubleShieldOptions) => { 36 | const { content, link, label, color, ...config } = options; 37 | const url = qs.stringifyUrl({ 38 | query: pickBy(config, identity) as any, 39 | url: urlJoin( 40 | SHIELD_BADGE_URL, 41 | formatCustomLabel({ 42 | color: color as string, 43 | content, 44 | label, 45 | }), 46 | ), 47 | }); 48 | 49 | return genShield(content || label, url, link); 50 | }; 51 | 52 | interface WebsiteShieldOptions extends ShieldsBaseOptions { 53 | down_message: string; 54 | label: string; 55 | up_message: string; 56 | url: string; 57 | } 58 | 59 | export const genWebsiteShield = (options: WebsiteShieldOptions) => { 60 | const url = qs.stringifyUrl({ 61 | query: pickBy(options, identity) as any, 62 | url: urlJoin(SHIELD_URL, 'website'), 63 | }); 64 | 65 | return genShield(options.label, url, options.url); 66 | }; 67 | 68 | interface DiscordShieldOptions extends ShieldsBaseOptions { 69 | label: string; 70 | serverId: string; 71 | } 72 | 73 | export const genDiscordShield = (options: DiscordShieldOptions) => { 74 | const { serverId, link, ...config } = options; 75 | const query = pickBy(config, identity) as any; 76 | const url = qs.stringifyUrl({ 77 | query: { logo: 'discord', ...query }, 78 | url: urlJoin(SHIELD_URL, 'discord', String(serverId)), 79 | }); 80 | 81 | return genShield('discord', url, link); 82 | }; 83 | 84 | interface BilibiliShieldOptions extends ShieldsBaseOptions { 85 | label: string; 86 | uid: string; 87 | } 88 | 89 | export const genBilibiliShield = (options: BilibiliShieldOptions) => { 90 | const { uid, labelColor, logoColor, ...config } = options; 91 | const query = pickBy(config, identity) as any; 92 | const url = qs.stringifyUrl({ 93 | query: { 94 | label_color: labelColor, 95 | logo_color: logoColor, 96 | uid, 97 | ...query, 98 | }, 99 | url: 'https://bilistats.lonelyion.com/followers', 100 | }); 101 | const link = urlJoin('https://space.bilibili.com', uid); 102 | 103 | return genShield('bilibili', url, link); 104 | }; 105 | -------------------------------------------------------------------------------- /src/services/genDockerShield.ts: -------------------------------------------------------------------------------- 1 | import { identity, pickBy } from 'lodash-es'; 2 | import qs from 'query-string'; 3 | import urlJoin from 'url-join'; 4 | 5 | import { DockerShieldControlItem, dockerShieldControls } from '@/const/dockerShieldControls'; 6 | import { DockerShieldBaseOptions } from '@/types/shields'; 7 | import { genShield } from '@/utils/genShield'; 8 | 9 | interface DockerShieldOptions extends DockerShieldBaseOptions, DockerShieldControlItem { 10 | name: string; 11 | } 12 | 13 | export const genDockerShield = (options: DockerShieldOptions) => { 14 | const { packageName, url, suffix, name, genLink, ...config } = options; 15 | 16 | const formatUrl = [url, packageName, suffix].filter(Boolean) as string[]; 17 | const defShield = qs.stringifyUrl({ 18 | query: pickBy(config, identity) as any, 19 | url: urlJoin(...formatUrl), 20 | }); 21 | const defLink = genLink?.(packageName); 22 | 23 | return genShield(`docker-${name}`, defShield, defLink); 24 | }; 25 | 26 | export const genDockerShields = ( 27 | options: Partial | any, 28 | pickOptions: { [key: string]: boolean }, 29 | ) => { 30 | const defShields: string[] = []; 31 | const defLinks: string[] = []; 32 | 33 | for (const [name, config] of Object.entries(dockerShieldControls)) { 34 | if (!pickOptions[name]) continue; 35 | const data = genDockerShield({ name, ...options, ...config }); 36 | defShields.push(data[0]); 37 | defLinks.push(data[1]); 38 | } 39 | return [defShields.join('\n'), defLinks.join('\n')]; 40 | }; 41 | -------------------------------------------------------------------------------- /src/services/genGithubShield.ts: -------------------------------------------------------------------------------- 1 | import { identity, pickBy } from 'lodash-es'; 2 | import qs from 'query-string'; 3 | import urlJoin from 'url-join'; 4 | 5 | import { 6 | GithubShieldControlItem, 7 | githubShieldControls, 8 | githubSocialControls, 9 | } from '@/const/githubShieldControls'; 10 | import { 11 | GITHUBE_CONTRIB_URL, 12 | GITHUB_STAR_HISTORY_URL, 13 | GITHUB_URL, 14 | SHIELD_GITHUB_URL, 15 | } from '@/const/url'; 16 | import { GithubShieldBaseOptions } from '@/types/shields'; 17 | import { genShield } from '@/utils/genShield'; 18 | import { genThemeModeImg } from '@/utils/genThemeModeImg'; 19 | 20 | export interface GithubShieldOptions extends GithubShieldBaseOptions, GithubShieldControlItem { 21 | name: string; 22 | } 23 | 24 | export const genGithubShield = (options: GithubShieldOptions) => { 25 | const { owner, repo, branch, name, url, suffix, genLink, ...config } = options; 26 | 27 | const formatUrl = [url, owner, repo, suffix].filter(Boolean) as string[]; 28 | const defShield = qs.stringifyUrl({ 29 | query: pickBy(config, identity) as any, 30 | url: urlJoin(...formatUrl), 31 | }); 32 | const defLink = genLink?.({ branch, owner, repo }); 33 | 34 | return genShield(`github-${name}`, defShield, defLink); 35 | }; 36 | 37 | export const genGithubSocialShields = ( 38 | options: Partial | any, 39 | pickOptions: { [key: string]: boolean }, 40 | ) => { 41 | const defShields: string[] = []; 42 | const defLinks: string[] = []; 43 | 44 | for (const [name, config] of Object.entries(githubSocialControls)) { 45 | if (!pickOptions[name]) continue; 46 | const data = genGithubShield({ name, ...options, ...config }); 47 | defShields.push(data[0]); 48 | defLinks.push(data[1]); 49 | } 50 | return [defShields.join('\n'), defLinks.join('\n')]; 51 | }; 52 | 53 | export const genGithubReleaseShields = ( 54 | options: Partial | any, 55 | pickOptions: { [key: string]: boolean }, 56 | ) => { 57 | const defShields: string[] = []; 58 | const defLinks: string[] = []; 59 | 60 | for (const [name, config] of Object.entries(githubShieldControls)) { 61 | if (!pickOptions[name]) continue; 62 | const data = genGithubShield({ name, ...options, ...config }); 63 | defShields.push(data[0]); 64 | defLinks.push(data[1]); 65 | } 66 | return [defShields.join('\n'), defLinks.join('\n')]; 67 | }; 68 | 69 | interface GithubActionShieldOptions extends GithubShieldBaseOptions { 70 | workflow: string; 71 | } 72 | 73 | export const genGithubActionShield = (options: GithubActionShieldOptions) => { 74 | const { owner, repo, workflow, ...config } = options; 75 | 76 | const query = pickBy(config, identity) as any; 77 | 78 | const defShield = qs.stringifyUrl({ 79 | query: { label: workflow, logo: 'githubactions', logoColor: 'white', ...query }, 80 | url: urlJoin(SHIELD_GITHUB_URL, 'actions/workflow/status', owner, repo, workflow + '.yml'), 81 | }); 82 | const defLink = urlJoin(GITHUB_URL, owner, repo, 'actions/workflows', workflow + '.yml'); 83 | 84 | return genShield(`github-action-${workflow}`, defShield, defLink); 85 | }; 86 | 87 | export const genGithubActionsShield = (options: GithubActionShieldOptions) => { 88 | const { workflow, ...config } = options; 89 | const workflows = workflow.replaceAll(' ', '').replaceAll(',', ',').split(','); 90 | const defShields: string[] = []; 91 | const defLinks: string[] = []; 92 | 93 | for (const w of workflows) { 94 | const data = genGithubActionShield({ workflow: w, ...config }); 95 | defShields.push(data[0]); 96 | defLinks.push(data[1]); 97 | } 98 | 99 | return [defShields.join('\n'), defLinks.join('\n')]; 100 | }; 101 | 102 | export const genGithubStarHistoryShield = (options: { owner: string; repo: string }) => { 103 | const light = qs.stringifyUrl({ 104 | query: { 105 | repos: `${options.owner}/${options.repo}`, 106 | type: 'Date', 107 | }, 108 | url: GITHUB_STAR_HISTORY_URL, 109 | }); 110 | const dark = qs.stringifyUrl({ 111 | query: { 112 | repos: `${options.owner}/${options.repo}`, 113 | theme: 'dark', 114 | type: 'Date', 115 | }, 116 | url: GITHUB_STAR_HISTORY_URL, 117 | }); 118 | return genThemeModeImg({ dark, light }); 119 | }; 120 | export const GenGithubContributorsShield = (options: { owner: string; repo: string }) => { 121 | const defShield = qs.stringifyUrl({ 122 | query: { 123 | repo: `${options.owner}/${options.repo}`, 124 | }, 125 | url: GITHUBE_CONTRIB_URL, 126 | }); 127 | const defLink = urlJoin(GITHUB_URL, options.owner, options.repo, 'graphs/contributors'); 128 | return genShield('github-contrib', defShield, defLink); 129 | }; 130 | 131 | export const GenGithubCodespaceShield = (options: { owner: string; repo: string }) => { 132 | const defShield = 'https://github.com/codespaces/badge.svg'; 133 | const defLink = urlJoin('https://codespaces.new', options.owner, options.repo); 134 | return genShield('github-codespace', defShield, defLink); 135 | }; 136 | -------------------------------------------------------------------------------- /src/services/genMarkdownContributing.ts: -------------------------------------------------------------------------------- 1 | import urlJoin from 'url-join'; 2 | 3 | import { prWelcomeShields } from '@/const/sample'; 4 | import { GITHUB_URL } from '@/const/url'; 5 | import { GenGithubContributorsShield } from '@/services/genGithubShield'; 6 | import { addBackToTop } from '@/utils/addBackTopTop'; 7 | 8 | interface MarkdownContributingOptions { 9 | backToTop?: boolean; 10 | owner: string; 11 | prWelcome: string; 12 | repo: string; 13 | } 14 | 15 | export const genMarkdownContributing = (options: MarkdownContributingOptions) => { 16 | const { owner, repo, prWelcome, backToTop } = options; 17 | 18 | const [prShield, prLink] = prWelcomeShields(prWelcome, owner, repo); 19 | const [contributorsShield, contributorsLink] = GenGithubContributorsShield({ owner, repo }); 20 | 21 | const md = ` 22 | ## 🤝 Contributing 23 | 24 | Contributions of all types are more than welcome, if you are interested in contributing code, feel free to check out our GitHub [Issues][github-issues-link] to get stuck in to show us what you’re made of. 25 | 26 | ${prShield} 27 | 28 | ${contributorsShield} 29 | `; 30 | const ref = [ 31 | `[github-issues-link]: ${urlJoin(GITHUB_URL, owner, repo, 'issues')}`, 32 | prLink, 33 | contributorsLink, 34 | ] 35 | .filter(Boolean) 36 | .join('\n'); 37 | 38 | const content = [md, ref]; 39 | 40 | return backToTop ? addBackToTop(content) : content; 41 | }; 42 | -------------------------------------------------------------------------------- /src/services/genMarkdownCredits.ts: -------------------------------------------------------------------------------- 1 | import { addBackToTop } from '@/utils/addBackTopTop'; 2 | 3 | interface MarkdownCreditsOptions { 4 | backToTop?: boolean; 5 | title: string; 6 | } 7 | 8 | export const genMarkdownCredits = (options: MarkdownCreditsOptions, value: string) => { 9 | const { title, backToTop } = options; 10 | 11 | const content = [[`## 🔗 ${title}`, value].join('\n\n'), '']; 12 | 13 | return backToTop ? addBackToTop(content) : content; 14 | }; 15 | -------------------------------------------------------------------------------- /src/services/genMarkdownDevelopment.ts: -------------------------------------------------------------------------------- 1 | import { bunShields } from '@/const/sample'; 2 | import { GenGithubCodespaceShield } from '@/services/genGithubShield'; 3 | import { addBackToTop } from '@/utils/addBackTopTop'; 4 | 5 | interface MarkdownDevelopmentOptions { 6 | backToTop?: boolean; 7 | bun: boolean; 8 | owner: string; 9 | repo: string; 10 | } 11 | export const genMarkdownDevelopment = (options: MarkdownDevelopmentOptions) => { 12 | const { owner, repo, bun, backToTop } = options; 13 | 14 | const [bunShield, bunLink] = bunShields; 15 | 16 | const [codespaceShield, codespaceLink] = GenGithubCodespaceShield({ owner, repo }); 17 | 18 | const md = ` 19 | ## ⌨️ Local Development 20 | 21 | You can use Github Codespaces for online development: 22 | 23 | ${codespaceShield} 24 | 25 | Or clone it for local development: 26 | 27 | ${bun ? bunShield : ''} 28 | 29 | \`\`\`bash 30 | $ git clone https://github.com/${owner}/${repo}.git 31 | $ cd ${repo} 32 | $ ${bun ? 'bun' : 'pnpm'} install 33 | $ ${bun ? 'bun' : 'pnpm'} dev 34 | \`\`\` 35 | `; 36 | 37 | const ref = [codespaceLink, bun && bunLink].filter(Boolean).join('\n'); 38 | 39 | const content = [md, ref]; 40 | 41 | return backToTop ? addBackToTop(content) : content; 42 | }; 43 | -------------------------------------------------------------------------------- /src/services/genMarkdownFeatures.ts: -------------------------------------------------------------------------------- 1 | import { addBackToTop } from '@/utils/addBackTopTop'; 2 | 3 | interface MarkdownFeaturesOptions { 4 | backToTop?: boolean; 5 | title: string; 6 | } 7 | 8 | export const genMarkdownFeatures = (options: MarkdownFeaturesOptions, value: string) => { 9 | const { title, backToTop } = options; 10 | 11 | const content = [[`## ✨ ${title}`, value].join('\n\n'), '']; 12 | 13 | return backToTop ? addBackToTop(content) : content; 14 | }; 15 | -------------------------------------------------------------------------------- /src/services/genMarkdownHero.ts: -------------------------------------------------------------------------------- 1 | import { githubSocialControlsPickList } from '@/const/githubShieldControls'; 2 | import { 3 | genGithubActionsShield, 4 | genGithubReleaseShields, 5 | genGithubSocialShields, 6 | } from '@/services/genGithubShield'; 7 | import { genNpmShields } from '@/services/genNpmShield'; 8 | 9 | interface MarkdownHeroOptions { 10 | backToTop?: boolean; 11 | banner: string; 12 | branch: string; 13 | description: string; 14 | logo: string; 15 | logo2?: string; 16 | owner: string; 17 | packageName: string; 18 | repo: string; 19 | title: string; 20 | workflow: string; 21 | } 22 | export const genMarkdownHero = (options: MarkdownHeroOptions) => { 23 | const { 24 | logo, 25 | logo2, 26 | title, 27 | description, 28 | banner, 29 | workflow, 30 | packageName, 31 | backToTop, 32 | owner, 33 | repo, 34 | branch, 35 | ...config 36 | } = options; 37 | 38 | const [releaseShields, releaseLinks] = genGithubReleaseShields( 39 | { owner, repo, ...config }, 40 | { 41 | release: !packageName, 42 | releaseDate: true, 43 | }, 44 | ); 45 | 46 | const [npmShield, npmLinks] = packageName 47 | ? genNpmShields( 48 | { packageName, ...config }, 49 | { 50 | release: true, 51 | }, 52 | ) 53 | : ['', '']; 54 | 55 | const [workflowShields, workflowLinks] = workflow 56 | ? genGithubActionsShield({ owner, repo, workflow, ...config }) 57 | : ['', '']; 58 | 59 | const [socialShields, socialLinks] = genGithubSocialShields( 60 | { branch, owner, repo, ...config }, 61 | githubSocialControlsPickList, 62 | ); 63 | 64 | const logoGroup = logo2 65 | ? [ 66 | ``, 67 | '', 68 | ``, 69 | ].join('\n') 70 | : ``; 71 | const firstShieldRow = [npmShield, releaseShields, workflowShields].filter(Boolean).join('\n'); 72 | const secondShieldRow = [socialShields].filter(Boolean).join('\n'); 73 | const shieldRows = [`${firstShieldRow}
`, secondShieldRow].filter(Boolean).join('\n'); 74 | 75 | const md = [ 76 | `
${backToTop ? '' : ''}`, 77 | logoGroup, 78 | `

${title}

`, 79 | description, 80 | shieldRows, 81 | '[Changelog](./CHANGELOG.md) · [Report Bug][github-issues-link] · [Request Feature][github-issues-link]', 82 | `![](${banner})`, 83 | '
', 84 | ].join('\n\n'); 85 | 86 | const ref = [npmLinks, releaseLinks, workflowLinks, socialLinks].join('\n'); 87 | 88 | const content = [md, ref]; 89 | 90 | return content; 91 | }; 92 | -------------------------------------------------------------------------------- /src/services/genMarkdownInstallation.ts: -------------------------------------------------------------------------------- 1 | import { bunShields } from '@/const/sample'; 2 | import { addBackToTop } from '@/utils/addBackTopTop'; 3 | 4 | interface MarkdownInstallationOptions { 5 | backToTop?: boolean; 6 | bun: boolean; 7 | esm: boolean; 8 | nextjs: boolean; 9 | packageName: string; 10 | } 11 | 12 | export const genMarkdownInstallation = (options: MarkdownInstallationOptions) => { 13 | const { esm, nextjs, packageName, bun, backToTop } = options; 14 | 15 | const [bunShield, bunLink] = bunShields; 16 | 17 | const esmBlock = ` 18 | > [!IMPORTANT]\\ 19 | > This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). 20 | `; 21 | 22 | const installBlock = ` 23 | To install \`${packageName}\`, run the following command: 24 | 25 | ${bun ? bunShield : ''} 26 | 27 | \`\`\`bash 28 | $ ${bun ? 'bun add' : 'pnpm install'} ${packageName} 29 | \`\`\` 30 | `; 31 | 32 | const nextjsBlock = ` 33 | ### Compile with Next.js 34 | 35 | > [!NOTE]\\ 36 | > By work correct with Next.js SSR, add \`transpilePackages: ['${packageName}']\` to \`next.config.js\`. For example: 37 | 38 | \`\`\`js 39 | const nextConfig = { 40 | transpilePackages: ['${packageName}'], 41 | }; 42 | \`\`\` 43 | `; 44 | 45 | const md = ['## 📦 Installation', esm && esmBlock, installBlock, nextjs && nextjsBlock] 46 | .filter(Boolean) 47 | .join('\n\n'); 48 | const ref = [bun && bunLink].filter(Boolean).join('\n'); 49 | 50 | const content = [md, ref]; 51 | 52 | return backToTop ? addBackToTop(content) : content; 53 | }; 54 | -------------------------------------------------------------------------------- /src/services/genMarkdownLicense.ts: -------------------------------------------------------------------------------- 1 | import urlJoin from 'url-join'; 2 | 3 | import { GITHUB_URL } from '@/const/url'; 4 | 5 | interface MarkdownLicenseOptions { 6 | license?: string; 7 | owner: string; 8 | repo: string; 9 | } 10 | 11 | export const genMarkdownLicense = (options: MarkdownLicenseOptions) => { 12 | const { owner, license } = options; 13 | 14 | const profileTitle = `[${owner === 'lobehub' ? 'LobeHub' : owner}][profile-link]`; 15 | const profileLink = `[profile-link]: ${urlJoin(GITHUB_URL, owner)}`; 16 | 17 | const md = ` 18 | --- 19 | 20 | #### 📝 License 21 | 22 | Copyright © ${new Date().getFullYear()} ${profileTitle}.
23 | This project is [${license || 'MIT'}](./LICENSE) licensed. 24 | `; 25 | const ref = [profileLink].filter(Boolean).join('\n'); 26 | 27 | const content = [md, ref]; 28 | 29 | return content; 30 | }; 31 | -------------------------------------------------------------------------------- /src/services/genNpmShield.ts: -------------------------------------------------------------------------------- 1 | import { identity, pickBy } from 'lodash-es'; 2 | import qs from 'query-string'; 3 | import urlJoin from 'url-join'; 4 | 5 | import { NpmShieldControlItem, npmShieldControls } from '@/const/npmShieldControls'; 6 | import { NpmShieldBaseOptions } from '@/types/shields'; 7 | import { genShield } from '@/utils/genShield'; 8 | 9 | interface NpmShieldOptions extends NpmShieldBaseOptions, NpmShieldControlItem { 10 | name: string; 11 | } 12 | 13 | export const genNpmShield = (options: NpmShieldOptions) => { 14 | const { packageName, url, suffix, name, genLink, ...config } = options; 15 | 16 | const formatUrl = [url, packageName, suffix].filter(Boolean) as string[]; 17 | const defShield = qs.stringifyUrl({ 18 | query: pickBy(config, identity) as any, 19 | url: urlJoin(...formatUrl), 20 | }); 21 | const defLink = genLink?.(packageName); 22 | 23 | return genShield(`npm-${name}`, defShield, defLink); 24 | }; 25 | 26 | export const genNpmShields = ( 27 | options: Partial | any, 28 | pickOptions: { [key: string]: boolean }, 29 | ) => { 30 | const defShields: string[] = []; 31 | const defLinks: string[] = []; 32 | 33 | for (const [name, config] of Object.entries(npmShieldControls)) { 34 | if (!pickOptions[name]) continue; 35 | const data = genNpmShield({ name, ...options, ...config }); 36 | defShields.push(data[0]); 37 | defLinks.push(data[1]); 38 | } 39 | return [defShields.join('\n'), defLinks.join('\n')]; 40 | }; 41 | -------------------------------------------------------------------------------- /src/services/genShareShield.ts: -------------------------------------------------------------------------------- 1 | import { identity, pickBy } from 'lodash-es'; 2 | import qs from 'query-string'; 3 | import urlJoin from 'url-join'; 4 | 5 | import { shareShieldControls, shareShieldControlsItem } from '@/const/shareShieldControls'; 6 | import { SHIELD_BADGE_URL } from '@/const/url'; 7 | import { ShieldsBaseOptions } from '@/types/shields'; 8 | import { formatCustomLabel } from '@/utils/formatCustomLabel'; 9 | import { genShield } from '@/utils/genShield'; 10 | 11 | type ShareShieldOptions = ShieldsBaseOptions & 12 | shareShieldControlsItem & { 13 | desc?: string; 14 | hashtags?: string; 15 | name: string; 16 | title?: string; 17 | url?: string; 18 | }; 19 | 20 | export const genShareShield = (options: ShareShieldOptions) => { 21 | const { name, genLink, title, desc, hashtags, url, color, label, ...config } = options; 22 | 23 | const defShield = qs.stringifyUrl({ 24 | query: pickBy(config, identity) as any, 25 | url: urlJoin( 26 | SHIELD_BADGE_URL, 27 | formatCustomLabel({ 28 | color: (color as string) || 'black', 29 | label: `share on ${label}`, 30 | }), 31 | ), 32 | }); 33 | const defLink = genLink?.({ desc, hashtags, title, url }); 34 | 35 | return genShield(`share-${name}`, defShield, defLink); 36 | }; 37 | 38 | export const genShareShields = ( 39 | options: Partial | any, 40 | pickOptions: { [key: string]: boolean }, 41 | ) => { 42 | const defShields: string[] = []; 43 | const defLinks: string[] = []; 44 | 45 | for (const [name, config] of Object.entries(shareShieldControls)) { 46 | if (!pickOptions[name]) continue; 47 | const data = genShareShield({ 48 | name, 49 | ...config, 50 | ...options, 51 | color: options.color || config.color, 52 | label: name, 53 | }); 54 | defShields.push(data[0]); 55 | defLinks.push(data[1]); 56 | } 57 | return [defShields.join('\n'), defLinks.join('\n')]; 58 | }; 59 | -------------------------------------------------------------------------------- /src/services/genSocialShield.ts: -------------------------------------------------------------------------------- 1 | import { identity, pickBy } from 'lodash-es'; 2 | import qs from 'query-string'; 3 | import urlJoin from 'url-join'; 4 | 5 | import { socialShieldControls, socialShieldControlsItem } from '@/const/socialShieldControls'; 6 | import { SHIELD_BADGE_URL } from '@/const/url'; 7 | import { ShieldsBaseOptions } from '@/types/shields'; 8 | import { formatCustomLabel } from '@/utils/formatCustomLabel'; 9 | import { genShield } from '@/utils/genShield'; 10 | 11 | interface SpcialIdOptions { 12 | discord?: string; 13 | qq?: string; 14 | steam?: string; 15 | wechat?: string; 16 | weibo?: string; 17 | x?: string; 18 | } 19 | 20 | type SocialShieldOptions = ShieldsBaseOptions & 21 | socialShieldControlsItem & { 22 | id: string; 23 | name: string; 24 | prefix: boolean; 25 | }; 26 | 27 | export const genSocialShield = (options: SocialShieldOptions) => { 28 | const { name, genLink, id, color, prefix, ...config } = options; 29 | 30 | const defShield = qs.stringifyUrl({ 31 | query: pickBy(config, identity) as any, 32 | url: urlJoin( 33 | SHIELD_BADGE_URL, 34 | formatCustomLabel({ 35 | color: (color as string) || 'black', 36 | label: `${prefix ? '@' : ''}${id}`, 37 | }), 38 | ), 39 | }); 40 | const defLink = genLink?.(id); 41 | 42 | return genShield(`social-${name}`, defShield, defLink); 43 | }; 44 | 45 | export const genSocialShields = ( 46 | options: Partial | any, 47 | idOptions: SpcialIdOptions, 48 | pickOptions: { [key: string]: boolean }, 49 | ) => { 50 | const defShields: string[] = []; 51 | const defLinks: string[] = []; 52 | 53 | for (const [name, config] of Object.entries(socialShieldControls)) { 54 | if (!pickOptions[name]) continue; 55 | const data = genSocialShield({ 56 | name, 57 | ...config, 58 | ...options, 59 | color: options.color || config.color, 60 | // @ts-ignore 61 | id: idOptions?.[name], 62 | }); 63 | defShields.push(data[0]); 64 | defLinks.push(data[1]); 65 | } 66 | return [defShields.join('\n'), defLinks.join('\n')]; 67 | }; 68 | -------------------------------------------------------------------------------- /src/services/genSponsor.tsx: -------------------------------------------------------------------------------- 1 | import { ImageResponse } from '@vercel/og'; 2 | 3 | import Sponsor from '@/Sponsor'; 4 | import { caleHeight, fetchFonts } from '@/Sponsor/utils'; 5 | import { fetchSponsors } from '@/services/sponsorkit'; 6 | 7 | export const MULTIPLE = 2; 8 | 9 | export const genSponsor = async ({ 10 | avatarSize = 64 * MULTIPLE, 11 | width = 800 * MULTIPLE, 12 | themeMode = 'dark', 13 | }: { 14 | avatarSize?: number; 15 | themeMode: 'dark' | 'light'; 16 | width?: number; 17 | }): Promise => { 18 | const data = await fetchSponsors({ 19 | github: { 20 | login: process.env.SPONSORKIT_GITHUB_LOGIN || '', 21 | token: process.env.SPONSORKIT_GITHUB_TOKEN || '', 22 | type: process.env.SPONSORKIT_GITHUB_TYPE || 'organization', 23 | }, 24 | includePastSponsors: true, 25 | opencollective: { 26 | key: process.env.SPONSORKIT_OPENCOLLECTIVE_KEY || '', 27 | slug: process.env.SPONSORKIT_OPENCOLLECTIVE_ID || 'lobehub', 28 | type: process.env.SPONSORKIT_OPENCOLLECTIVE_TYPE || 'collective', 29 | }, 30 | }); 31 | 32 | const { fontBold, fontRegular } = await fetchFonts(); 33 | const height = caleHeight(data, { avatarSize, width } as any); 34 | 35 | return new ImageResponse( 36 | ( 37 | 44 | ), 45 | { 46 | emoji: 'fluent', 47 | fonts: [ 48 | { 49 | data: fontRegular, 50 | name: 'HarmonyOS Sans', 51 | style: 'normal', 52 | weight: 400, 53 | }, 54 | { 55 | data: fontBold, 56 | name: 'HarmonyOS Sans', 57 | style: 'normal', 58 | weight: 600, 59 | }, 60 | ], 61 | headers: { 62 | 'CDN-Cache-Control': 'public, s-maxage=120', 63 | 'Vercel-CDN-Cache-Control': 'public, s-maxage=3600', 64 | 'cache-control': 'public, max-age=120, s-maxage=120', 65 | 'content-type': 'image/png', 66 | }, 67 | height, 68 | width, 69 | }, 70 | ); 71 | }; 72 | -------------------------------------------------------------------------------- /src/services/genVercelShield.ts: -------------------------------------------------------------------------------- 1 | import { identity, pickBy } from 'lodash-es'; 2 | import qs from 'query-string'; 3 | import urlJoin from 'url-join'; 4 | 5 | import { GITHUB_URL, SHIELD_URL } from '@/const/url'; 6 | import { genShield } from '@/utils/genShield'; 7 | 8 | interface VercelDeployShieldOptions { 9 | env?: string; 10 | envDescription?: string; 11 | envLink?: string; 12 | owner: string; 13 | repo: string; 14 | } 15 | 16 | export const GenVercelDeployShield = (options: VercelDeployShieldOptions) => { 17 | const { owner, repo, env, envDescription, envLink } = options; 18 | const query = { 19 | env, 20 | envDescription, 21 | envLink, 22 | ['project-name']: repo, 23 | ['repository-link']: urlJoin(GITHUB_URL, owner, repo), 24 | ['repository-name']: repo, 25 | }; 26 | const defShield = 'https://vercel.com/button'; 27 | const defLink = qs.stringifyUrl({ 28 | query: pickBy(query, identity), 29 | url: 'https://vercel.com/new/clone', 30 | }); 31 | return genShield('vercel-deploy', defShield, defLink); 32 | }; 33 | 34 | export const genVercelWebsiteShield = (options: { label: string; url: string }) => { 35 | const url = qs.stringifyUrl({ 36 | query: pickBy( 37 | { 38 | down_message: 'offline', 39 | labelColor: 'black', 40 | logo: 'vercel', 41 | style: 'flat-square', 42 | up_message: 'online', 43 | ...options, 44 | }, 45 | identity, 46 | ) as any, 47 | url: urlJoin(SHIELD_URL, 'website'), 48 | }); 49 | 50 | return genShield('vercel-website', url, options.url); 51 | }; 52 | -------------------------------------------------------------------------------- /src/services/sponsorkit/github/index.ts: -------------------------------------------------------------------------------- 1 | import type { Provider, SponsorkitConfig, Sponsorship } from '../types'; 2 | import { makeQuery } from './makeQuery'; 3 | 4 | const API = 'https://api.github.com/graphql'; 5 | 6 | export async function fetchGitHubSponsors( 7 | token: string, 8 | login: string, 9 | type: string, 10 | config: SponsorkitConfig, 11 | ): Promise { 12 | if (!token) throw new Error('GitHub token is required'); 13 | if (!login) throw new Error('GitHub login is required'); 14 | if (!['user', 'organization'].includes(type)) 15 | throw new Error('GitHub type must be either `user` or `organization`'); 16 | 17 | const sponsors: Sponsorship[] = []; 18 | let cursor; 19 | 20 | do { 21 | const query = makeQuery(login, type, !config.includePastSponsors, cursor); 22 | const res = await fetch(API, { 23 | body: JSON.stringify({ query }), 24 | headers: { 25 | 'Authorization': `bearer ${token}`, 26 | 'Content-Type': 'application/json', 27 | }, 28 | method: 'POST', 29 | }); 30 | 31 | const data = await res.json(); 32 | 33 | if (!data) throw new Error(`Get no response on requesting ${API}`); 34 | else if (data.errors?.[0]?.type === 'INSUFFICIENT_SCOPES') 35 | throw new Error('Token is missing the `read:user` and/or `read:org` scopes'); 36 | else if (data.errors?.length) 37 | throw new Error(`GitHub API error:\n${JSON.stringify(data.errors, null, 2)}`); 38 | 39 | sponsors.push(...(data.data[type].sponsorshipsAsMaintainer.nodes || [])); 40 | if (data.data[type].sponsorshipsAsMaintainer.pageInfo.hasNextPage) 41 | cursor = data.data[type].sponsorshipsAsMaintainer.pageInfo.endCursor; 42 | else cursor = undefined; 43 | } while (cursor); 44 | 45 | const processed = sponsors.map( 46 | (raw: any): Sponsorship => ({ 47 | createdAt: raw.createdAt, 48 | isOneTime: raw.tier.isOneTime, 49 | monthlyDollars: raw.tier.monthlyPriceInDollars, 50 | privacyLevel: raw.privacyLevel, 51 | sponsor: { 52 | ...raw.sponsorEntity, 53 | __typename: undefined, 54 | linkUrl: `https://github.com/${raw.sponsorEntity.login}`, 55 | type: raw.sponsorEntity.__typename, 56 | }, 57 | tierName: raw.tier.name, 58 | }), 59 | ); 60 | 61 | return processed; 62 | } 63 | 64 | export const GitHubProvider: Provider = { 65 | fetchSponsors(config) { 66 | return fetchGitHubSponsors( 67 | config.github?.token || config.token!, 68 | config.github?.login || config.login!, 69 | config.github?.type || 'user', 70 | config, 71 | ); 72 | }, 73 | name: 'github', 74 | }; 75 | -------------------------------------------------------------------------------- /src/services/sponsorkit/github/makeQuery.ts: -------------------------------------------------------------------------------- 1 | const graphql = String.raw; 2 | 3 | export function makeQuery(login: string, type: string, activeOnly = true, cursor?: string) { 4 | return graphql`{ 5 | ${type}(login: "${login}") { 6 | sponsorshipsAsMaintainer(activeOnly: ${Boolean(activeOnly)}, first: 100${cursor ? ` after: "${cursor}"` : ''}) { 7 | totalCount 8 | pageInfo { 9 | endCursor 10 | hasNextPage 11 | } 12 | nodes { 13 | createdAt 14 | privacyLevel 15 | isActive 16 | tier { 17 | name 18 | isOneTime 19 | monthlyPriceInCents 20 | monthlyPriceInDollars 21 | } 22 | sponsorEntity { 23 | __typename 24 | ...on Organization { 25 | login 26 | name 27 | avatarUrl 28 | websiteUrl 29 | } 30 | ...on User { 31 | login 32 | name 33 | avatarUrl 34 | websiteUrl 35 | } 36 | } 37 | } 38 | } 39 | } 40 | }`; 41 | } 42 | -------------------------------------------------------------------------------- /src/services/sponsorkit/index.ts: -------------------------------------------------------------------------------- 1 | import { GitHubProvider } from './github'; 2 | import { OpenCollectiveProvider } from './opencollective'; 3 | import type { Provider, ProviderName, SponsorkitConfig } from './types'; 4 | 5 | export * from './github'; 6 | 7 | export const ProvidersMap = { 8 | github: GitHubProvider, 9 | opencollective: OpenCollectiveProvider, 10 | }; 11 | 12 | export function guessProviders(config: SponsorkitConfig) { 13 | const items: ProviderName[] = []; 14 | if (config.github && config.github.login) items.push('github'); 15 | 16 | if (config.patreon && config.patreon.token) items.push('patreon'); 17 | 18 | if ( 19 | config.opencollective && 20 | (config.opencollective.id || config.opencollective.slug || config.opencollective.githubHandle) 21 | ) 22 | items.push('opencollective'); 23 | 24 | if (config.afdian && config.afdian.userId && config.afdian.token) items.push('afdian'); 25 | 26 | // fallback 27 | if (!items.length) items.push('github'); 28 | 29 | return items; 30 | } 31 | 32 | export function resolveProviders(names: (ProviderName | Provider)[]) { 33 | return [...new Set(names)].map((i) => { 34 | if (typeof i === 'string') { 35 | // @ts-ignore 36 | const provider = ProvidersMap[i]; 37 | if (!provider) throw new Error(`Unknown provider: ${i}`); 38 | return provider; 39 | } 40 | return i; 41 | }); 42 | } 43 | 44 | export async function fetchSponsors(config: SponsorkitConfig) { 45 | const providers = resolveProviders(guessProviders(config)); 46 | const sponsorships = await Promise.all( 47 | providers.map((provider) => provider.fetchSponsors(config)), 48 | ); 49 | 50 | return sponsorships.flat(1); 51 | } 52 | -------------------------------------------------------------------------------- /src/services/sponsorkit/opencollective/index.ts: -------------------------------------------------------------------------------- 1 | import type { Provider, Sponsorship } from '../types'; 2 | import { makeSubscriptionsQuery, makeTransactionsQuery } from './makeQuery'; 3 | 4 | interface SocialLink { 5 | type: string; 6 | url: string; 7 | } 8 | 9 | const API = 'https://api.opencollective.com/graphql/v2/'; 10 | 11 | /** 12 | * Get the best URL from a list of social links. 13 | * The best URL is the first URL in a priority order, 14 | * with WEBSITE being the highest priority. 15 | * The rest of the order is somewhat arbitrary. 16 | * 17 | * @param socialLinks List of social links 18 | * @returns The best URL or `undefined` if no URL is found 19 | * @see makeQuery 20 | */ 21 | function getBestUrl(socialLinks: SocialLink[]): string | undefined { 22 | const urls = socialLinks 23 | .filter( 24 | (i) => 25 | i.type === 'WEBSITE' || 26 | i.type === 'GITHUB' || 27 | i.type === 'GITLAB' || 28 | i.type === 'TWITTER' || 29 | i.type === 'FACEBOOK' || 30 | i.type === 'YOUTUBE' || 31 | i.type === 'INSTAGRAM' || 32 | i.type === 'LINKEDIN' || 33 | i.type === 'DISCORD' || 34 | i.type === 'TUMBLR', 35 | ) 36 | .map((i) => i.url); 37 | 38 | return urls[0]; 39 | } 40 | 41 | /** 42 | * Get the account type from the API values. 43 | * 44 | * @param type The type of the account from the API 45 | * @returns The account type 46 | */ 47 | function getAccountType(type: string): 'User' | 'Organization' { 48 | switch (type) { 49 | case 'INDIVIDUAL': { 50 | return 'User'; 51 | } 52 | case 'ORGANIZATION': 53 | case 'COLLECTIVE': 54 | case 'FUND': 55 | case 'PROJECT': 56 | case 'EVENT': 57 | case 'VENDOR': 58 | case 'BOT': { 59 | return 'Organization'; 60 | } 61 | default: { 62 | throw new Error(`Unknown account type: ${type}`); 63 | } 64 | } 65 | } 66 | 67 | function createSponsorFromTransaction( 68 | transaction: any, 69 | excludeOrders: string[], 70 | ): [string, Sponsorship] | undefined { 71 | const slug = transaction.fromAccount.slug; 72 | if (slug === 'github-sponsors') 73 | // ignore github sponsors 74 | return undefined; 75 | 76 | if (excludeOrders.includes(transaction.order?.id)) return undefined; 77 | 78 | let monthlyDollars: number = transaction.amount.value; 79 | if (transaction.order?.status !== 'ACTIVE') { 80 | const firstDayOfCurrentMonth = new Date( 81 | new Date().getUTCFullYear(), 82 | new Date().getUTCMonth(), 83 | 1, 84 | ); 85 | if (new Date(transaction.createdAt) < firstDayOfCurrentMonth) monthlyDollars = -1; 86 | } else if (transaction.order?.frequency === 'MONTHLY') { 87 | monthlyDollars = transaction.order?.amount.value; 88 | } else if (transaction.order?.frequency === 'YEARLY') { 89 | monthlyDollars = transaction.order?.amount.value / 12; 90 | } 91 | 92 | const sponsor: Sponsorship = { 93 | createdAt: 94 | transaction.order?.frequency === 'ONETIME' 95 | ? transaction.createdAt 96 | : transaction.order?.createdAt, 97 | isOneTime: transaction.order?.frequency === 'ONETIME', 98 | monthlyDollars, 99 | privacyLevel: transaction.fromAccount.isIncognito ? 'PRIVATE' : 'PUBLIC', 100 | raw: transaction, 101 | sponsor: { 102 | avatarUrl: transaction.fromAccount.imageUrl, 103 | linkUrl: `https://opencollective.com/${slug}`, 104 | login: slug, 105 | name: transaction.fromAccount.name, 106 | type: getAccountType(transaction.fromAccount.type), 107 | websiteUrl: getBestUrl(transaction.fromAccount.socialLinks), 108 | }, 109 | tierName: transaction.tier?.name, 110 | }; 111 | 112 | return [transaction.fromAccount.id, sponsor]; 113 | } 114 | 115 | function createSponsorFromOrder(order: any): [string, Sponsorship] | undefined { 116 | const slug = order.fromAccount.slug; 117 | if (slug === 'github-sponsors') 118 | // ignore github sponsors 119 | return undefined; 120 | 121 | let monthlyDollars: number = order.amount.value; 122 | if (order.status !== 'ACTIVE') monthlyDollars = -1; 123 | else 124 | switch (order.frequency) { 125 | case 'MONTHLY': { 126 | monthlyDollars = order.amount.value; 127 | break; 128 | } 129 | case 'YEARLY': { 130 | monthlyDollars = order.amount.value / 12; 131 | break; 132 | } 133 | case 'ONETIME': { 134 | { 135 | monthlyDollars = order.amount.value; 136 | // No default 137 | } 138 | break; 139 | } 140 | } 141 | 142 | const sponsor: Sponsorship = { 143 | createdAt: order.frequency === 'ONETIME' ? order.createdAt : order.order?.createdAt, 144 | isOneTime: order.frequency === 'ONETIME', 145 | monthlyDollars, 146 | privacyLevel: order.fromAccount.isIncognito ? 'PRIVATE' : 'PUBLIC', 147 | raw: order, 148 | sponsor: { 149 | avatarUrl: order.fromAccount.imageUrl, 150 | linkUrl: `https://opencollective.com/${slug}`, 151 | login: slug, 152 | name: order.fromAccount.name, 153 | type: getAccountType(order.fromAccount.type), 154 | websiteUrl: getBestUrl(order.fromAccount.socialLinks), 155 | }, 156 | tierName: order.tier?.name, 157 | }; 158 | 159 | return [order.fromAccount.id, sponsor]; 160 | } 161 | 162 | export async function fetchOpenCollectiveSponsors( 163 | key?: string, 164 | id?: string, 165 | slug?: string, 166 | githubHandle?: string, 167 | includePastSponsors?: boolean, 168 | ): Promise { 169 | if (!key) throw new Error('OpenCollective api key is required'); 170 | if (!slug && !id && !githubHandle) 171 | throw new Error('OpenCollective collective id or slug or GitHub handle is required'); 172 | 173 | const sponsors: any[] = []; 174 | const monthlyTransactions: any[] = []; 175 | let offset; 176 | offset = 0; 177 | 178 | do { 179 | const query = makeSubscriptionsQuery(id, slug, githubHandle, offset, !includePastSponsors); 180 | const res = await fetch(API, { 181 | body: JSON.stringify({ query }), 182 | headers: { 183 | 'Api-Key': `${key}`, 184 | 'Content-Type': 'application/json', 185 | }, 186 | method: 'POST', 187 | }); 188 | const data = await res.json(); 189 | const nodes = data.data.account.orders.nodes; 190 | const totalCount = data.data.account.orders.totalCount; 191 | 192 | sponsors.push(...(nodes || [])); 193 | 194 | if (nodes.length !== 0) { 195 | if (totalCount > offset + nodes.length) offset += nodes.length; 196 | else offset = undefined; 197 | } else { 198 | offset = undefined; 199 | } 200 | } while (offset); 201 | 202 | offset = 0; 203 | do { 204 | const now: Date = new Date(); 205 | const dateFrom: Date | undefined = includePastSponsors 206 | ? undefined 207 | : new Date(now.getFullYear(), now.getMonth(), 1); 208 | const query = makeTransactionsQuery(id, slug, githubHandle, offset, dateFrom); 209 | const res = await fetch(API, { 210 | body: JSON.stringify({ query }), 211 | headers: { 212 | 'Api-Key': `${key}`, 213 | 'Content-Type': 'application/json', 214 | }, 215 | method: 'POST', 216 | }); 217 | const data = await res.json(); 218 | const nodes = data.data.account.transactions.nodes; 219 | const totalCount = data.data.account.transactions.totalCount; 220 | 221 | monthlyTransactions.push(...(nodes || [])); 222 | if (nodes.length !== 0) { 223 | if (totalCount > offset + nodes.length) offset += nodes.length; 224 | else offset = undefined; 225 | } else { 226 | offset = undefined; 227 | } 228 | } while (offset); 229 | 230 | const sponsorships: [string, Sponsorship][] = sponsors 231 | .map((element) => createSponsorFromOrder(element)) 232 | .filter((sponsorship): sponsorship is [string, Sponsorship] => sponsorship !== null); 233 | 234 | const monthlySponsorships: [string, Sponsorship][] = monthlyTransactions 235 | .map((t) => 236 | createSponsorFromTransaction( 237 | t, 238 | sponsorships.map((i) => i[1].raw.id), 239 | ), 240 | ) 241 | .filter( 242 | (sponsorship): sponsorship is [string, Sponsorship] => 243 | sponsorship !== null && sponsorship !== undefined, 244 | ); 245 | 246 | const transactionsBySponsorId: Map = monthlySponsorships.reduce( 247 | (map, [id, sponsor]) => { 248 | const existingSponsor = map.get(id); 249 | if (existingSponsor) { 250 | const createdAt = new Date(sponsor.createdAt!); 251 | const existingSponsorCreatedAt = new Date(existingSponsor.createdAt!); 252 | if (createdAt >= existingSponsorCreatedAt) map.set(id, sponsor); 253 | else if ( 254 | new Date( 255 | existingSponsorCreatedAt.getFullYear(), 256 | existingSponsorCreatedAt.getMonth(), 257 | 1, 258 | ) === new Date(createdAt.getFullYear(), createdAt.getMonth(), 1) 259 | ) 260 | existingSponsor.monthlyDollars += sponsor.monthlyDollars; 261 | } else { 262 | map.set(id, sponsor); 263 | } 264 | 265 | return map; 266 | }, 267 | new Map(), 268 | ); 269 | 270 | const processed: Map = sponsorships.reduce((map, [id, sponsor]) => { 271 | const existingSponsor = map.get(id); 272 | if (existingSponsor) { 273 | const createdAt = new Date(sponsor.createdAt!); 274 | const existingSponsorCreatedAt = new Date(existingSponsor.createdAt!); 275 | if (createdAt >= existingSponsorCreatedAt) map.set(id, sponsor); 276 | } else { 277 | map.set(id, sponsor); 278 | } 279 | return map; 280 | }, new Map()); 281 | 282 | const result: Sponsorship[] = [...processed.values(), ...transactionsBySponsorId.values()]; 283 | return result; 284 | } 285 | 286 | export const OpenCollectiveProvider: Provider = { 287 | fetchSponsors(config) { 288 | return fetchOpenCollectiveSponsors( 289 | config.opencollective?.key, 290 | config.opencollective?.id, 291 | config.opencollective?.slug, 292 | config.opencollective?.githubHandle, 293 | config.includePastSponsors, 294 | ); 295 | }, 296 | name: 'opencollective', 297 | }; 298 | -------------------------------------------------------------------------------- /src/services/sponsorkit/opencollective/makeQuery.ts: -------------------------------------------------------------------------------- 1 | const graphql = String.raw; 2 | 3 | /** 4 | * Make a partial query for the OpenCollective API. 5 | * This is used to query for either a collective or an account. 6 | * If `id` is set, it will query for a collective. 7 | * If `slug` is set, it will query for an account. 8 | * If `githubHandle` is set, it will query for an account. 9 | * If none of the above are set, an error will be thrown. 10 | * 11 | * @param id Collective id 12 | * @param slug Collective slug 13 | * @param githubHandle GitHub handle 14 | * @returns The partial query 15 | * @see makeQuery 16 | * @see fetchOpenCollectiveSponsors 17 | */ 18 | function makeAccountQueryPartial(id?: string, slug?: string, githubHandle?: string) { 19 | if (id) return `id: "${id}"`; 20 | else if (slug) return `slug: "${slug}"`; 21 | else if (githubHandle) return `githubHandle: "${githubHandle}"`; 22 | else throw new Error('OpenCollective collective id or slug or GitHub handle is required'); 23 | } 24 | 25 | export function makeTransactionsQuery( 26 | id?: string, 27 | slug?: string, 28 | githubHandle?: string, 29 | offset?: number, 30 | dateFrom?: Date, 31 | dateTo?: Date, 32 | ) { 33 | const accountQueryPartial = makeAccountQueryPartial(id, slug, githubHandle); 34 | const dateFromParam = dateFrom ? `, dateFrom: "${dateFrom.toISOString()}"` : ''; 35 | const dateToParam = dateTo ? `, dateTo: "${dateTo.toISOString()}"` : ''; 36 | return graphql`{ 37 | account(${accountQueryPartial}) { 38 | transactions(limit: 1000, offset:${offset}, type: CREDIT ${dateFromParam} ${dateToParam}) { 39 | offset 40 | limit 41 | totalCount 42 | nodes { 43 | type 44 | kind 45 | id 46 | order { 47 | id 48 | status 49 | frequency 50 | tier { 51 | name 52 | } 53 | amount { 54 | value 55 | } 56 | } 57 | createdAt 58 | amount { 59 | value 60 | } 61 | fromAccount { 62 | name 63 | id 64 | slug 65 | type 66 | socialLinks { 67 | url 68 | type 69 | } 70 | isIncognito 71 | imageUrl(height: 460, format: png) 72 | } 73 | } 74 | } 75 | } 76 | }`; 77 | } 78 | 79 | export function makeSubscriptionsQuery( 80 | id?: string, 81 | slug?: string, 82 | githubHandle?: string, 83 | offset?: number, 84 | activeOnly?: boolean, 85 | ) { 86 | const activeOrNot = activeOnly ? 'onlyActiveSubscriptions: true' : 'onlySubscriptions: true'; 87 | return graphql`{ 88 | account(${makeAccountQueryPartial(id, slug, githubHandle)}) { 89 | orders(limit: 1000, offset:${offset}, ${activeOrNot}, filter: INCOMING) { 90 | nodes { 91 | id 92 | createdAt 93 | frequency 94 | status 95 | tier { 96 | name 97 | } 98 | amount { 99 | value 100 | } 101 | totalDonations { 102 | value 103 | } 104 | createdAt 105 | fromAccount { 106 | name 107 | id 108 | slug 109 | type 110 | socialLinks { 111 | url 112 | type 113 | } 114 | isIncognito 115 | imageUrl(height: 460, format: png) 116 | } 117 | } 118 | } 119 | } 120 | }`; 121 | } 122 | -------------------------------------------------------------------------------- /src/services/sponsorkit/types.ts: -------------------------------------------------------------------------------- 1 | export interface Provider { 2 | fetchSponsors: (config: SponsorkitConfig) => Promise; 3 | name: string; 4 | } 5 | 6 | export interface Sponsor { 7 | avatarUrl: string; 8 | avatarUrlHighRes?: string; 9 | avatarUrlLowRes?: string; 10 | avatarUrlMediumRes?: string; 11 | linkUrl?: string; 12 | login: string; 13 | name: string | null; 14 | type: 'User' | 'Organization'; 15 | websiteUrl?: string; 16 | } 17 | 18 | export interface Sponsorship { 19 | createdAt?: string; 20 | expireAt?: string; 21 | isOneTime?: boolean; 22 | monthlyDollars: number; 23 | privacyLevel?: 'PUBLIC' | 'PRIVATE'; 24 | provider?: ProviderName | string; 25 | /** 26 | * Raw data from provider 27 | */ 28 | raw?: any; 29 | sponsor: Sponsor; 30 | tierName?: string; 31 | } 32 | 33 | export type OutputFormat = 'svg' | 'png' | 'json'; 34 | 35 | export type ProviderName = 'github' | 'patreon' | 'opencollective' | 'afdian'; 36 | 37 | export interface ProvidersConfig { 38 | afdian?: { 39 | /** 40 | * Exchange rate of USD to CNY 41 | * 42 | * @default 6.5 43 | */ 44 | exechangeRate?: number; 45 | /** 46 | * Afdian Token that have access to your sponsorships. 47 | * 48 | * Will read from `SPONSORKIT_AFDIAN_TOKEN` environment variable if not set. 49 | * 50 | * @see https://afdian.net/dashboard/dev 51 | * @deprecated It's not recommended set this value directly, pass from env or use `.env` file. 52 | */ 53 | token?: string; 54 | /** 55 | * The userId of your Afdian. 56 | * 57 | * Will read from `SPONSORKIT_AFDIAN_USER_ID` environment variable if not set. 58 | * 59 | * @see https://afdian.net/dashboard/dev 60 | */ 61 | userId?: string; 62 | }; 63 | github?: { 64 | /** 65 | * User id of your GitHub account. 66 | * 67 | * Will read from `SPONSORKIT_GITHUB_LOGIN` environment variable if not set. 68 | */ 69 | login?: string; 70 | /** 71 | * GitHub Token that have access to your sponsorships. 72 | * 73 | * Will read from `SPONSORKIT_GITHUB_TOKEN` environment variable if not set. 74 | * 75 | * @deprecated It's not recommended set this value directly, pass from env or use `.env` file. 76 | */ 77 | token?: string; 78 | /** 79 | * The account type for sponsorships. 80 | * 81 | * Possible values are `user`(default) and `organization`. 82 | * Will read from `SPONSORKIT_GITHUB_TYPE` environment variable if not set. 83 | */ 84 | type?: string; 85 | }; 86 | opencollective?: { 87 | /** 88 | * The GitHub handle of your account. 89 | * 90 | * Will read from `SPONSORKIT_OPENCOLLECTIVE_GH_HANDLE` environment variable if not set. 91 | */ 92 | githubHandle?: string; 93 | /** 94 | * The id of your account. 95 | * 96 | * Will read from `SPONSORKIT_OPENCOLLECTIVE_ID` environment variable if not set. 97 | */ 98 | id?: string; 99 | /** 100 | * Api key of your OpenCollective account. 101 | * 102 | * Will read from `SPONSORKIT_OPENCOLLECTIVE_KEY` environment variable if not set. 103 | * 104 | * @deprecated It's not recommended set this value directly, pass from env or use `.env` file. 105 | */ 106 | key?: string; 107 | /** 108 | * The slug of your account. 109 | * 110 | * Will read from `SPONSORKIT_OPENCOLLECTIVE_SLUG` environment variable if not set. 111 | */ 112 | slug?: string; 113 | /* 114 | * The type of your account. (`collective` or `individual`) 115 | * 116 | * Will read from `SPONSORKIT_OPENCOLLECTIVE_TYPE` environment variable if not set. 117 | */ 118 | type?: string; 119 | }; 120 | patreon?: { 121 | /** 122 | * Patreon Token that have access to your sponsorships. 123 | * 124 | * Will read from `SPONSORKIT_PATREON_TOKEN` environment variable if not set. 125 | * 126 | * @deprecated It's not recommended set this value directly, pass from env or use `.env` file. 127 | */ 128 | token?: string; 129 | }; 130 | } 131 | 132 | export interface SponsorkitConfig extends ProvidersConfig { 133 | /** 134 | * Filter of sponsorships to render in the final image. 135 | */ 136 | filter?: (sponsor: Sponsorship, all: Sponsorship[]) => boolean | void; 137 | 138 | /** 139 | * By pass cache 140 | */ 141 | force?: boolean; 142 | 143 | /** 144 | * Output formats 145 | * 146 | * @default ['json', 'svg', 'png'] 147 | */ 148 | formats?: OutputFormat[]; 149 | 150 | /** 151 | * Whether to display the past sponsors 152 | * Currently only works with GitHub provider 153 | * 154 | * @default auto detect based on tiers 155 | */ 156 | includePastSponsors?: boolean; 157 | 158 | /** 159 | * Whether to display the private sponsors 160 | * 161 | * @default false 162 | */ 163 | includePrivate?: boolean; 164 | 165 | /** 166 | * @deprecated use `github.login` instead 167 | */ 168 | login?: string; 169 | 170 | /** 171 | * Name of exported files 172 | * 173 | * @default 'sponsors' 174 | */ 175 | name?: string | null; 176 | 177 | /** 178 | * Padding of image container 179 | */ 180 | padding?: { 181 | bottom?: number; 182 | top?: number; 183 | }; 184 | 185 | /** 186 | * @default auto detect based on the config provided 187 | */ 188 | providers?: ProviderName[]; 189 | 190 | /** 191 | * Inline CSS of generated SVG 192 | */ 193 | svgInlineCSS?: string; 194 | 195 | /** 196 | * Tiers 197 | */ 198 | tiers?: Tier[]; 199 | 200 | /** 201 | * @deprecated use `github.token` instead 202 | */ 203 | token?: string; 204 | 205 | /** 206 | * Width of the image. 207 | * 208 | * @default 700 209 | */ 210 | width?: number; 211 | } 212 | 213 | export interface Tier { 214 | monthlyDollars?: number; 215 | padding?: { 216 | bottom?: number; 217 | top?: number; 218 | }; 219 | title?: string; 220 | } 221 | -------------------------------------------------------------------------------- /src/store/action.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobehub/lobe-readme-wizard/6600570486025bd6733df0fdb0748c0d100ca992/src/store/action.ts -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobehub/lobe-readme-wizard/6600570486025bd6733df0fdb0748c0d100ca992/src/store/index.ts -------------------------------------------------------------------------------- /src/store/initialState.ts: -------------------------------------------------------------------------------- 1 | import { ThemeMode } from 'antd-style'; 2 | 3 | export enum Tab { 4 | Readme = 'readme', 5 | Shields = 'shields', 6 | } 7 | 8 | export interface StoreState { 9 | activeTab: Tab; 10 | themeMode: ThemeMode; 11 | } 12 | 13 | export const initialState: StoreState = { 14 | activeTab: Tab.Readme, 15 | themeMode: 'auto', 16 | }; 17 | -------------------------------------------------------------------------------- /src/store/selectors.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobehub/lobe-readme-wizard/6600570486025bd6733df0fdb0748c0d100ca992/src/store/selectors.ts -------------------------------------------------------------------------------- /src/store/store.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobehub/lobe-readme-wizard/6600570486025bd6733df0fdb0748c0d100ca992/src/store/store.ts -------------------------------------------------------------------------------- /src/types/i18next.d.ts: -------------------------------------------------------------------------------- 1 | import 'i18next'; 2 | 3 | import { NS } from '@/types/resources'; 4 | 5 | declare module 'i18next' { 6 | interface CustomTypeOptions { 7 | defaultNS: 'common'; 8 | resources: NS; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/types/shields.ts: -------------------------------------------------------------------------------- 1 | export interface ShieldsBaseOptions { 2 | color?: string; 3 | label?: string; 4 | labelColor?: string; 5 | link?: string; 6 | logo?: string; 7 | logoColor?: string; 8 | style: 'flat' | 'flat-square' | 'plastic' | 'for-the-badge' | 'social' | string; 9 | } 10 | 11 | export interface GithubShieldBaseOptions { 12 | branch?: string; 13 | owner: string; 14 | repo: string; 15 | } 16 | 17 | export interface NpmShieldBaseOptions { 18 | packageName: string; 19 | } 20 | 21 | export interface DockerShieldBaseOptions { 22 | packageName: string; 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/addBackTopTop.ts: -------------------------------------------------------------------------------- 1 | const backToTopShields = [ 2 | ['
', '[![][back-to-top]](#readme-top)', '
'].join('\n\n'), 3 | `[back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-black?style=flat-square`, 4 | ]; 5 | 6 | export const addBackToTop = (content: string[]) => { 7 | const [md, ref] = content; 8 | const [shield, link] = backToTopShields; 9 | return [[md, shield].join('\n\n'), [ref, link].join('\n')]; 10 | }; 11 | -------------------------------------------------------------------------------- /src/utils/formatCustomLabel.ts: -------------------------------------------------------------------------------- 1 | export const formatCustomLabel = ({ 2 | content, 3 | label, 4 | color, 5 | }: { 6 | color?: string; 7 | content?: string; 8 | label: string; 9 | }) => { 10 | const data = encodeURIComponent([content, label, color || 'white'].filter(Boolean).join('-')); 11 | return content ? data : `-${data}`; 12 | }; 13 | -------------------------------------------------------------------------------- /src/utils/genPickList.ts: -------------------------------------------------------------------------------- 1 | export const genPickList = (obj: { [key: string]: any }): { [key: string]: boolean } => { 2 | const list: { [key: string]: boolean } = {}; 3 | 4 | for (const key of Object.keys(obj)) { 5 | list[key] = true; 6 | } 7 | 8 | return list; 9 | }; 10 | -------------------------------------------------------------------------------- /src/utils/genShield.ts: -------------------------------------------------------------------------------- 1 | import { kebabCase } from 'lodash-es'; 2 | 3 | export const genShield = (alt: string, url: string, link?: string) => { 4 | const shieldName = kebabCase([alt.toLowerCase(), 'shield'].filter(Boolean).join('-')); 5 | if (!link) return [`![][${shieldName}]`, `[${shieldName}]: ${url}`]; 6 | const linkName = kebabCase([alt.toLowerCase(), 'link'].filter(Boolean).join('-')); 7 | return [ 8 | `[![][${shieldName}]][${linkName}]`, 9 | [`[${shieldName}]: ${url}`, `[${linkName}]: ${link}`].join('\n'), 10 | ]; 11 | }; 12 | -------------------------------------------------------------------------------- /src/utils/genSiteHeadTitle.ts: -------------------------------------------------------------------------------- 1 | export const genSiteHeadTitle = (title: string = 'Readme Generator') => 2 | title ? `${title} · LobeHub` : 'LobeHub'; 3 | -------------------------------------------------------------------------------- /src/utils/genThemeModeImg.ts: -------------------------------------------------------------------------------- 1 | export const genThemeModeImg = ({ light, dark }: { dark: string; light: string }) => { 2 | return [ 3 | '', 4 | ` `, 5 | ` `, 6 | ``, 7 | ].join('\n'); 8 | }; 9 | -------------------------------------------------------------------------------- /src/utils/remarkFormat.ts: -------------------------------------------------------------------------------- 1 | import { remark } from 'remark'; 2 | import remarkGfm from 'remark-gfm'; 3 | 4 | export const remarkFormat = async (md: string): Promise => { 5 | const data = await remark() 6 | .use(remarkGfm) 7 | .use({ 8 | settings: { 9 | bullet: '-', 10 | emphasis: '*', 11 | fences: true, 12 | rule: '-', 13 | strong: '*', 14 | tightDefinitions: true, 15 | }, 16 | }) 17 | .process(md.trim()); 18 | 19 | return data.toString(); 20 | }; 21 | -------------------------------------------------------------------------------- /tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobehub/lobe-readme-wizard/6600570486025bd6733df0fdb0748c0d100ca992/tests/.gitkeep -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "declaration": true, 5 | "downlevelIteration": true, 6 | "esModuleInterop": true, 7 | "jsx": "react-jsx", 8 | "lib": ["dom", "dom.iterable", "esnext"], 9 | "paths": { 10 | "@@/*": [".dumi/tmp/*"], 11 | "@/*": ["src/*"], 12 | "@lobehub/readme-wizard": ["src"], 13 | "@lobehub/readme-wizard/*": ["src/*"] 14 | }, 15 | "resolveJsonModule": true, 16 | "skipLibCheck": true, 17 | "strict": true, 18 | "types": ["vitest/globals"] 19 | }, 20 | "include": ["src", "docs", "tests", ".dumirc.ts", "*.ts"] 21 | } 22 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "installCommand": "/bun1/bun install" 3 | } 4 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'node:path'; 2 | import { defineConfig } from 'vitest/config'; 3 | 4 | export default defineConfig({ 5 | test: { 6 | alias: { 7 | '@': resolve(__dirname, './src'), 8 | }, 9 | coverage: { 10 | provider: 'v8', 11 | reporter: ['text', 'json', 'lcov', 'text-summary'], 12 | }, 13 | environment: 'jsdom', 14 | globals: true, 15 | }, 16 | }); 17 | --------------------------------------------------------------------------------