From 8aca96041b75aae90e605a2ce91d66f39fbbfd12 Mon Sep 17 00:00:00 2001 From: canisminor1990 Date: Wed, 12 Jul 2023 17:18:55 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20Improve=20functionality=20a?= =?UTF-8?q?nd=20user=20experience?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Delete unnecessary files and components - Update README files - Modify code components for better performance - Handle input and selection more effectively - Generate commit messages using AI - Add and remove links as needed - Rename variables for clarity - Modify descriptions and types of commit messages These changes aim to improve the functionality and user experience of the project. --- README-zh_CN.md | 297 ------------------ README.md | 6 +- package.json | 1 + packages/lobe-cli-ui/README.md | 11 + .../lobe-cli-ui/src/BorderView/index.test.tsx | 17 - packages/lobe-cli-ui/src/BorderView/index.tsx | 24 -- .../lobe-cli-ui/src/Header/index.test.tsx | 12 - packages/lobe-cli-ui/src/Header/index.tsx | 18 -- .../lobe-cli-ui/src/SelectInput/index.tsx | 6 +- .../src/{View => SplitView}/index.test.tsx | 8 +- .../src/{View => SplitView}/index.tsx | 6 +- packages/lobe-cli-ui/src/Tabs/index.test.tsx | 21 -- packages/lobe-cli-ui/src/Tabs/index.tsx | 23 -- .../src/TabsWithHeader/index.test.tsx | 22 -- .../lobe-cli-ui/src/TabsWithHeader/index.tsx | 33 -- packages/lobe-cli-ui/src/index.ts | 6 +- packages/lobe-commit/README-zh_CN.md | 6 +- packages/lobe-commit/README.md | 4 +- .../lobe-commit/src/commands/Ai/index.tsx | 32 +- .../src/commands/Commit/AiCommit.tsx | 85 +++-- .../src/commands/Commit/InputIssues.tsx | 22 +- .../src/commands/Commit/InputIssuesType.tsx | 54 ++++ .../src/commands/Commit/InputScope.tsx | 108 ++++++- .../src/commands/Commit/InputType.tsx | 6 +- .../lobe-commit/src/commands/Commit/index.tsx | 6 +- .../lobe-commit/src/commands/List/index.tsx | 8 +- .../lobe-commit/src/constants/gitmojis.ts | 46 +-- .../lobe-commit/src/constants/template.ts | 2 +- packages/lobe-commit/src/store/commitStore.ts | 22 +- packages/lobe-commit/src/utils/genAiCommit.ts | 85 +++-- .../lobe-commit/src/utils/genCommitMessage.ts | 69 +++- 31 files changed, 428 insertions(+), 638 deletions(-) delete mode 100644 README-zh_CN.md delete mode 100644 packages/lobe-cli-ui/src/BorderView/index.test.tsx delete mode 100644 packages/lobe-cli-ui/src/BorderView/index.tsx delete mode 100644 packages/lobe-cli-ui/src/Header/index.test.tsx delete mode 100644 packages/lobe-cli-ui/src/Header/index.tsx rename packages/lobe-cli-ui/src/{View => SplitView}/index.test.tsx (75%) rename packages/lobe-cli-ui/src/{View => SplitView}/index.tsx (77%) delete mode 100644 packages/lobe-cli-ui/src/Tabs/index.test.tsx delete mode 100644 packages/lobe-cli-ui/src/Tabs/index.tsx delete mode 100644 packages/lobe-cli-ui/src/TabsWithHeader/index.test.tsx delete mode 100644 packages/lobe-cli-ui/src/TabsWithHeader/index.tsx create mode 100644 packages/lobe-commit/src/commands/Commit/InputIssuesType.tsx diff --git a/README-zh_CN.md b/README-zh_CN.md deleted file mode 100644 index b528e6e..0000000 --- a/README-zh_CN.md +++ /dev/null @@ -1,297 +0,0 @@ - - -
- - - - - -

Lobe Commit

- -Lobe Commit 是一款使用 ChatGPT 生成基于 Gitmoji 的 CLI 提交工具 - -[English](./README.md) · 简体中文 · [Changelog](./packages/lobe-commit/CHANGELOG.md) · [Report Bug][issues-url] · [Request Feature][issues-url] - - - -[![release][release-shield]][release-url] ![][release-download-shield] [![releaseDate][release-date-shield]][release-date-url] [![ciTest][ci-test-shield]][ci-test-url] [![ciRelease][ci-release-shield]][ci-release-url]
[![contributors][contributors-shield]][contributors-url] [![forks][forks-shield]][forks-url] [![stargazers][stargazers-shield]][stargazers-url] [![issues][issues-shield]][issues-url] - -
- -![](https://raw.githubusercontent.com/canisminor1990/lobe-commit/master/docs/preview.webp) - -
-文档目录 - -#### TOC - -- [✨ 特性](#-特性) - -- [📦 安装](#-安装) - -- [🤯 使用](#-使用) - - - [Git hook](#git-hook) - - [配置](#配置) - - [选项](#选项) - -- [⌨️ 本地开发](#️-本地开发) - -- [🤝 参与贡献](#-参与贡献) - -- [🔗 链接](#-链接) - -#### - -
- -## ✨ 特性 - -- 🤯 支持使用 ChatGPT 根据 git diffs 自动生成提交信息 -- 🛠️ 流畅的提交信息编辑流程 -- 😜 支持添加 Gitmoji -- 📝 支持 Conventional Commits 规范 -- ⚡️ 支持拉取 issues 列表并便捷绑定 -- 💄 支持自定义 Prompt -- 🗺️ 支持多语言提交信息 - -
- -[![][back-to-top]](#readme-top) - -
- -## 📦 安装 - -要安装 Lobe Commit,请运行以下命令: - -```bash -npm install -g @lobehub/commit-cli -``` - -> 👉 提示:请确保 Node.js 版本 >= 18 - -
- -[![][back-to-top]](#readme-top) - -
- -## 🤯 使用 - -使用 `lobe-commit` 命令为暂生成提交信息信息: - -```shell -$ git add -$ lobe-commit -``` - -> 👉 提示:如果认为 `lobe-commit` 太长了,可以使用`lobe`别名 - -
- -#### AI 模式 - -在 AI 模式下,可以使用 ChatGPT 生成完整的提交信息 - -> 👉 提示:需要在设置中 `lobe-commit -o` 配置 OpenAI 令牌,同时如果有特殊的网络要求,也可以在设置中配置 OpenAI 的转发地址 -> -> ![](https://raw.githubusercontent.com/canisminor1990/lobe-commit/master/docs/preview-ai.webp) - -
- -#### 编辑器模式 - -在编辑器模式下,可以通过简单的流程生成 `(): []` 格式的提交信息 - -> 👉 提示:如果项目是 GitHub Repo,则将自动获取该仓库的 issues,可以使用 空格 选择多个问题将其链接到提交信息中 - -![](https://raw.githubusercontent.com/canisminor1990/lobe-commit/master/docs/preview-editor.webp) - -
- -[![][back-to-top]](#readme-top) - -
- -### Git hook - -可以通过 `prepare-commit-msg`钩子将 Lobe Commit 与 Git 集成, 允许像往常一样使用 Git 并在提交之前编辑提交信息 - -
- -#### 安装 - -要在项目中安装 hook,请运行以下命令: - -```shell -$ lobe-commit --init # 或使用短标志 -i -``` - -
- -#### 卸载 - -要从项目中卸载 hook,请运行以下命令: - -```shell -$ lobe-commit --remove # 或使用短标志 -r -``` - -
- -[![][back-to-top]](#readme-top) - -
- -### 配置 - -要配置 Lobe Commit,请运行以下命令: - -```shell -$ lobe-commit --config # 或使用短标志 -o -``` - -- 要使用 AI 自动生成,需要在设置中填写 [OpenAI 令牌](https://platform.openai.com/account/api-keys) -- 要自动拉取私人仓库 issues,需要在设置中填写具有 repo 权限的 [GitHub 令牌](https://github.com/settings/tokens) - -
- -[![][back-to-top]](#readme-top) - -
- -### 选项 - -Lobe Commit 支持以下选项: - -```shell ---commit -c 使用提示交互式提交 ---config -o 设置lobe-commit首选项 ---help -h 打印基本选项 ---init -i 将lobe-commit初始化为提交钩子 ---remove -r 删除先前初始化的提交钩子 ---list -l 列出所有可用的提交类型 ---version -v 打印lobe-commit安装版本 -``` - -
- -[![][back-to-top]](#readme-top) - -
- -## ⌨️ 本地开发 - -可以使用 Gitpod 进行在线开发: - -[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)][gitpod-url] - -或者,可以克隆存储库并运行以下命令进行本地开发: - -```bash -$ git clone https://github.com/canisminor1990/lobe-commit.git -$ cd lobe-commit -$ npm install -$ npm start -``` - -
- -[![][back-to-top]](#readme-top) - -
- -## 🤝 参与贡献 - - - -> 📊 总计:**3** - - - - - - - - - - - - - -
- -[![][back-to-top]](#readme-top) - -
- -## 🔗 链接 - -- gitmoji-cli: -- ai-commit: - -
- -[![][back-to-top]](#readme-top) - -
- ---- - -#### 📝 License - -Copyright © 2023 [CanisMinor][profile-url].
This project is [MIT](./LICENSE) licensed. - - - -[profile-url]: https://github.com/canisminor1990 -[gitpod-url]: https://gitpod.io/#https://github.com/canisminor1990/lobe-commit - - - -[back-to-top]: https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square - - - -[release-shield]: https://img.shields.io/npm/v/@lobehub/commit-cli?label=%F0%9F%A4%AF%20NPM -[release-url]: https://www.npmjs.com/package/@lobehub/commit-cli - - - -[release-download-shield]: https://img.shields.io/npm/dt/@lobehub/commit-cli - - - -[release-date-shield]: https://img.shields.io/github/release-date/canisminor1990/lobe-commit?style=flat -[release-date-url]: https://github.com/canisminor1990/lobe-commit/releases - - - -[ci-test-shield]: https://github.com/canisminor1990/lobe-commit/workflows/Test%20CI/badge.svg -[ci-test-url]: https://github.com/canisminor1990/lobe-commit/actions/workflows/test.yml - - - -[ci-release-shield]: https://github.com/canisminor1990/lobe-commit/workflows/Build%20and%20Release/badge.svg -[ci-release-url]: https://github.com/canisminor1990/lobe-commit/actions/workflows/release.yml - - - -[contributors-shield]: https://img.shields.io/github/contributors/canisminor1990/lobe-commit.svg?style=flat -[contributors-url]: https://github.com/canisminor1990/lobe-commit/graphs/contributors - - - -[forks-shield]: https://img.shields.io/github/forks/canisminor1990/lobe-commit.svg?style=flat -[forks-url]: https://github.com/canisminor1990/lobe-commit/network/members - - - -[stargazers-shield]: https://img.shields.io/github/stars/canisminor1990/lobe-commit.svg?style=flat -[stargazers-url]: https://github.com/canisminor1990/lobe-commit/stargazers - - - -[issues-shield]: https://img.shields.io/github/issues/canisminor1990/lobe-commit.svg?style=flat -[issues-url]: https://github.com/canisminor1990/lobe-commit/issues/new/choose diff --git a/README.md b/README.md index 0cabe72..dfd37c9 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ With AI mode, you can generate a complete commit message using ChatGPT. #### Editor mode -In Editor mode, you can choose the `(): []` format by following a simple flow. +In Editor mode, you can choose the `(): []` format by following a simple flow. Press TAB to go back to the previous step. > 👉 Tip: If your project is a GitHub repository, the Editor mode feature will automatically fetch the issues associated with your repository. You can select multiple issues to link to your commit message by space . @@ -228,8 +228,10 @@ $ npm start ## 🔗 Credits +- langchainjs: +- gitmoji-commit-workflow: - gitmoji-cli: -- ai-commit: +- ink:
diff --git a/package.json b/package.json index 539a4bc..c2c2023 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "prepare": "husky install", "prettier": "prettier -c --write \"**/**\"", "release": "multi-semantic-release", + "start": "npm run dev", "test": "lerna run test --parallel && npm run lint" }, "lint-staged": { diff --git a/packages/lobe-cli-ui/README.md b/packages/lobe-cli-ui/README.md index f03b32d..a833526 100644 --- a/packages/lobe-cli-ui/README.md +++ b/packages/lobe-cli-ui/README.md @@ -34,6 +34,7 @@ Lobe Cli UI is an open-source UI component library for building _AIGC_ cli apps - [📦 Installation](#-installation) - [⌨️ Local Development](#️-local-development) +- [🔗 Credits](#-credits) #### @@ -76,6 +77,16 @@ $ pnpm start
+## 🔗 Credits + +- ink: + +
+ +[![][back-to-top]](#readme-top) + +
+ --- #### 📝 License diff --git a/packages/lobe-cli-ui/src/BorderView/index.test.tsx b/packages/lobe-cli-ui/src/BorderView/index.test.tsx deleted file mode 100644 index f9234d5..0000000 --- a/packages/lobe-cli-ui/src/BorderView/index.test.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Text } from 'ink'; -import { render } from 'ink-testing-library'; -import { describe, expect, it } from 'vitest'; - -import BorderView from './index'; - -describe('BorderView', () => { - it('render', () => { - const { lastFrame } = render( - - children - , - ); - console.log(lastFrame()); - expect(lastFrame()?.includes('children')).toEqual(true); - }); -}); diff --git a/packages/lobe-cli-ui/src/BorderView/index.tsx b/packages/lobe-cli-ui/src/BorderView/index.tsx deleted file mode 100644 index 521b03a..0000000 --- a/packages/lobe-cli-ui/src/BorderView/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Box } from 'ink'; -import { type ReactNode, memo } from 'react'; - -import { useTheme } from '@/hooks/useTheme'; - -export interface BorderViewProps { - children: ReactNode; -} -const BorderView = memo(({ children }) => { - const theme = useTheme(); - return ( - - {children} - - ); -}); - -export default BorderView; diff --git a/packages/lobe-cli-ui/src/Header/index.test.tsx b/packages/lobe-cli-ui/src/Header/index.test.tsx deleted file mode 100644 index 4637ec2..0000000 --- a/packages/lobe-cli-ui/src/Header/index.test.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { render } from 'ink-testing-library'; -import { describe, expect, it } from 'vitest'; - -import Header from './index'; - -describe('Header', () => { - it('render', () => { - const { lastFrame } = render(
); - console.log(lastFrame()); - expect(lastFrame()?.includes('title')).toEqual(true); - }); -}); diff --git a/packages/lobe-cli-ui/src/Header/index.tsx b/packages/lobe-cli-ui/src/Header/index.tsx deleted file mode 100644 index 068c486..0000000 --- a/packages/lobe-cli-ui/src/Header/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Box, Text } from 'ink'; -import { memo } from 'react'; - -import { useTheme } from '@/hooks/useTheme'; - -export interface HeaderProps { - title: string; -} -const Header = memo(({ title }) => { - const theme = useTheme(); - return ( - - {title} - - ); -}); - -export default Header; diff --git a/packages/lobe-cli-ui/src/SelectInput/index.tsx b/packages/lobe-cli-ui/src/SelectInput/index.tsx index f698891..e8f259d 100644 --- a/packages/lobe-cli-ui/src/SelectInput/index.tsx +++ b/packages/lobe-cli-ui/src/SelectInput/index.tsx @@ -69,8 +69,8 @@ const SelectInput = memo( useInput( useCallback( - (input, key) => { - if (input === 'k' || key.upArrow) { + (_, key) => { + if (key.upArrow) { const lastIndex = (hasLimit ? limit : items.length) - 1; const atFirstIndex = selectedIndex === 0; const nextIndex = hasLimit ? selectedIndex : lastIndex; @@ -89,7 +89,7 @@ const SelectInput = memo( } } - if (input === 'j' || key.downArrow) { + if (key.downArrow) { const atLastIndex = selectedIndex === (hasLimit ? limit : items.length) - 1; const nextIndex = hasLimit ? selectedIndex : 0; const nextRotateIndex = atLastIndex ? rotateIndex - 1 : rotateIndex; diff --git a/packages/lobe-cli-ui/src/View/index.test.tsx b/packages/lobe-cli-ui/src/SplitView/index.test.tsx similarity index 75% rename from packages/lobe-cli-ui/src/View/index.test.tsx rename to packages/lobe-cli-ui/src/SplitView/index.test.tsx index e672923..c2ea9be 100644 --- a/packages/lobe-cli-ui/src/View/index.test.tsx +++ b/packages/lobe-cli-ui/src/SplitView/index.test.tsx @@ -2,14 +2,14 @@ import { Text } from 'ink'; import { render } from 'ink-testing-library'; import { describe, expect, it } from 'vitest'; -import { View } from '@/index'; +import { SplitView } from '@/index'; -describe('View', () => { +describe('SplitView', () => { it('render', () => { const { lastFrame } = render( - + children - , + , ); console.log(lastFrame()); expect(lastFrame()?.includes('children')).toEqual(true); diff --git a/packages/lobe-cli-ui/src/View/index.tsx b/packages/lobe-cli-ui/src/SplitView/index.tsx similarity index 77% rename from packages/lobe-cli-ui/src/View/index.tsx rename to packages/lobe-cli-ui/src/SplitView/index.tsx index b03f700..1230f40 100644 --- a/packages/lobe-cli-ui/src/View/index.tsx +++ b/packages/lobe-cli-ui/src/SplitView/index.tsx @@ -3,10 +3,10 @@ import { type ReactNode, memo } from 'react'; import { useTheme } from '@/hooks/useTheme'; -export interface ViewProps { +export interface SplitViewProps { children: ReactNode; } -const View = memo(({ children }) => { +const SplitView = memo(({ children }) => { const theme = useTheme(); return ( (({ children }) => { ); }); -export default View; +export default SplitView; diff --git a/packages/lobe-cli-ui/src/Tabs/index.test.tsx b/packages/lobe-cli-ui/src/Tabs/index.test.tsx deleted file mode 100644 index fabf91f..0000000 --- a/packages/lobe-cli-ui/src/Tabs/index.test.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Text } from 'ink'; -import { render } from 'ink-testing-library'; -import { describe, expect, it } from 'vitest'; - -import Tabs from './index'; - -describe('Tabs', () => { - it('render', () => { - const { lastFrame } = render( - one, key: 1 }, - { children: two, key: 2 }, - ]} - />, - ); - console.log(lastFrame()); - expect(lastFrame()?.includes('two')).toEqual(true); - }); -}); diff --git a/packages/lobe-cli-ui/src/Tabs/index.tsx b/packages/lobe-cli-ui/src/Tabs/index.tsx deleted file mode 100644 index 176fb43..0000000 --- a/packages/lobe-cli-ui/src/Tabs/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Fragment, type ReactNode, memo } from 'react'; - -export interface TabItem { - children: ReactNode; - key: string | number; -} - -export interface TabsProps { - activeKey: string | number; - items: TabItem[]; -} - -const Tabs = memo(({ activeKey, items }) => { - return ( - <> - {items.map( - (item) => activeKey === item.key && {item.children}, - )} - - ); -}); - -export default Tabs; diff --git a/packages/lobe-cli-ui/src/TabsWithHeader/index.test.tsx b/packages/lobe-cli-ui/src/TabsWithHeader/index.test.tsx deleted file mode 100644 index 31d7742..0000000 --- a/packages/lobe-cli-ui/src/TabsWithHeader/index.test.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Text } from 'ink'; -import { render } from 'ink-testing-library'; -import { describe, expect, it } from 'vitest'; - -import TabsWithHeader from './index'; - -describe('TabsWithHeader', () => { - it('render', () => { - const { lastFrame } = render( - one, key: 1, title: 'ONE' }, - { children: two, key: 2, title: 'TWO' }, - ]} - />, - ); - console.log(lastFrame()); - expect(lastFrame()?.includes('two')).toEqual(true); - expect(lastFrame()?.includes('TWO')).toEqual(true); - }); -}); diff --git a/packages/lobe-cli-ui/src/TabsWithHeader/index.tsx b/packages/lobe-cli-ui/src/TabsWithHeader/index.tsx deleted file mode 100644 index ba2e466..0000000 --- a/packages/lobe-cli-ui/src/TabsWithHeader/index.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { Fragment, type ReactNode, memo } from 'react'; - -import BorderView from '@/BorderView'; -import Header from '@/Header'; - -export interface TabsWithHeaderItem { - children: ReactNode; - key: string | number; - title: string; -} - -interface TabsWithHeaderProps { - activeKey: string | number; - items: TabsWithHeaderItem[]; -} - -const TabsWithHeader = memo(({ activeKey, items }) => { - return ( - <> - {items.map( - (item) => - activeKey === item.key && ( - -
- {item.children} - - ), - )} - - ); -}); - -export default TabsWithHeader; diff --git a/packages/lobe-cli-ui/src/index.ts b/packages/lobe-cli-ui/src/index.ts index 061c11c..3b85131 100644 --- a/packages/lobe-cli-ui/src/index.ts +++ b/packages/lobe-cli-ui/src/index.ts @@ -1,9 +1,5 @@ -export { default as BorderView, type BorderViewProps } from './BorderView'; export { default as ConfigPanel, type ConfigPanelItem, type ConfigPanelProps } from './ConfigPanel'; -export { default as Header, type HeaderProps } from './Header'; export { useTheme } from './hooks/useTheme'; export { default as Panel, type PanelProps } from './Panel'; export { default as SelectInput, type SelectInputItem, type SelectInputProps } from './SelectInput'; -export { type TabItem, default as Tabs, type TabsProps } from './Tabs'; -export { default as TabsWithHeader, type TabsWithHeaderItem } from './TabsWithHeader'; -export { default as View, type ViewProps } from './View'; +export { default as SplitView, type SplitViewProps } from './SplitView'; diff --git a/packages/lobe-commit/README-zh_CN.md b/packages/lobe-commit/README-zh_CN.md index 02e4e46..6585705 100644 --- a/packages/lobe-commit/README-zh_CN.md +++ b/packages/lobe-commit/README-zh_CN.md @@ -100,7 +100,7 @@ $ lobe-commit #### 编辑器模式 -在编辑器模式下,可以通过简单的流程生成 `(): []` 格式的提交信息 +在编辑器模式下,可以通过简单的流程生成 `(): []` 格式的提交信息,使用 TAB 返回上一步 > 👉 提示:如果项目是 GitHub Repo,则将自动获取该仓库的 issues,可以使用 空格 选择多个问题将其链接到提交信息中 @@ -202,8 +202,10 @@ $ npm start ## 🔗 链接 +- langchainjs: +- gitmoji-commit-workflow: - gitmoji-cli: -- ai-commit: +- ink:
diff --git a/packages/lobe-commit/README.md b/packages/lobe-commit/README.md index 1d66712..9d5ff9d 100644 --- a/packages/lobe-commit/README.md +++ b/packages/lobe-commit/README.md @@ -202,8 +202,10 @@ $ npm start ## 🔗 Credits +- langchainjs: +- gitmoji-commit-workflow: - gitmoji-cli: -- ai-commit: +- ink:
diff --git a/packages/lobe-commit/src/commands/Ai/index.tsx b/packages/lobe-commit/src/commands/Ai/index.tsx index fd3a926..1746075 100644 --- a/packages/lobe-commit/src/commands/Ai/index.tsx +++ b/packages/lobe-commit/src/commands/Ai/index.tsx @@ -1,25 +1,39 @@ import { Spinner } from '@inkjs/ui'; -import { useTheme } from '@lobehub/cli-ui'; -import { Box, Text } from 'ink'; -import { memo, useEffect, useState } from 'react'; +import { Panel, useTheme } from '@lobehub/cli-ui'; +import { Text } from 'ink'; +import { memo, useCallback, useEffect, useState } from 'react'; import genAiCommit from '@/utils/genAiCommit'; const Ai = memo(() => { const [loadingInfo, setLoadingInfo] = useState(' Generating...'); const [message, setMessage] = useState(''); + const [summary, setSummary] = useState(''); + const [loading, setLoading] = useState(true); const theme = useTheme(); + const handleGenerate = useCallback(() => { + genAiCommit({ setLoading, setLoadingInfo, setMessage, setSummary }); + }, []); + useEffect(() => { - genAiCommit(setLoadingInfo).then((text: any) => { - setMessage(text); - }); + handleGenerate(); }, []); return ( - - {message ? {message} : } - + + {`👉 DIFF SUMMARY: `} + {summary} + + ) + } + title={`🤯 AI Commit Generator`} + > + {!loading && message ? {message} : } + ); }); diff --git a/packages/lobe-commit/src/commands/Commit/AiCommit.tsx b/packages/lobe-commit/src/commands/Commit/AiCommit.tsx index bf6f713..ab1c298 100644 --- a/packages/lobe-commit/src/commands/Commit/AiCommit.tsx +++ b/packages/lobe-commit/src/commands/Commit/AiCommit.tsx @@ -1,7 +1,7 @@ import { Spinner } from '@inkjs/ui'; -import { Panel, SelectInput } from '@lobehub/cli-ui'; +import { Panel, SelectInput, type SelectInputItem, SplitView, useTheme } from '@lobehub/cli-ui'; import { Text, useInput } from 'ink'; -import { memo, useCallback, useEffect, useState } from 'react'; +import { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { useCommitStore } from '@/store/commitStore'; import genAiCommit from '@/utils/genAiCommit'; @@ -12,49 +12,74 @@ const AiCommit = memo(() => { setMessage: st.setMessage, setStep: st.setStep, })); - useInput(useCallback((_, key) => key.tab && setStep('feat'), [])); + useInput(useCallback((_, key) => key.tab && setStep('type'), [])); const [loading, setLoading] = useState(true); + const [summary, setSummary] = useState(''); const [loadingInfo, setLoadingInfo] = useState(' Generating...'); - const [count, setCount] = useState(0); - useEffect(() => { - genAiCommit(setLoadingInfo).then((text: any) => { - setMessage(text); - setLoading(false); - setLoadingInfo(' Generating...'); - }); - }, [count]); + const theme = useTheme(); + + const handleGenerate = useCallback((summary?: string) => { + genAiCommit({ cacheSummary: summary, setLoading, setLoadingInfo, setMessage, setSummary }); + }, []); const handleSelect = useCallback( (item: any) => { - if (item.value === 'reload') { - setMessage(''); - setCount(count + 1); - setLoading(true); - } else if (item.value === 'confirm') { - setStep('commit'); + switch (item.value) { + case 'reloadFromSummary': { + handleGenerate(summary); + break; + } + case 'reload': { + setSummary(''); + handleGenerate(); + break; + } + case 'edit': { + setStep('type'); + break; + } + case 'confirm': { + setStep('commit'); + break; + } } }, - [count], + [summary], + ); + + useEffect(() => { + handleGenerate(); + }, []); + + const items = useMemo( + () => + [ + summary && { + label: '🔄️ Regenerate commit message from summary [FAST]', + value: 'reloadFromSummary', + }, + { label: '🔄️ Regenerate full commit message [SLOW]', value: 'reload' }, + { label: '✏️ Edit this message', value: 'edit' }, + { label: '✅ Use this message', value: 'confirm' }, + ].filter(Boolean) as SelectInputItem[], + [summary], ); return ( - ) - } + footer={!loading && message && } reverse title={`🤯 AI Commit Generator`} > {!loading && message ? {message} : } + {summary && ( + + + {`👉 DIFF SUMMARY: `} + {summary} + + + )} ); }); diff --git a/packages/lobe-commit/src/commands/Commit/InputIssues.tsx b/packages/lobe-commit/src/commands/Commit/InputIssues.tsx index 52a1824..7a25e1e 100644 --- a/packages/lobe-commit/src/commands/Commit/InputIssues.tsx +++ b/packages/lobe-commit/src/commands/Commit/InputIssues.tsx @@ -1,5 +1,5 @@ import { MultiSelect, type MultiSelectProps, Spinner, TextInput } from '@inkjs/ui'; -import { Panel, View, useTheme } from '@lobehub/cli-ui'; +import { Panel, SplitView, useTheme } from '@lobehub/cli-ui'; import isEqual from 'fast-deep-equal'; import { Text, useInput } from 'ink'; import { debounce } from 'lodash-es'; @@ -56,14 +56,14 @@ const InputIssues = memo(() => { })) as any; }, [keywords, issueList]); - const handleKeywords = (v: string) => { + const handleKeywords = useCallback((v: string) => { setKeywords(v.replaceAll(' ', '')); - }; + }, []); - const handleSelect = (v: string[]) => { + const handleSelect = useCallback((v: string[]) => { setIssues(v.join(',')); setKeywords(''); - }; + }, []); const InputKeywords = useCallback( () => ( @@ -76,6 +76,10 @@ const InputIssues = memo(() => { [issues], ); + const handleSubmit = useCallback(() => { + setStep(issues ? 'issuesType' : 'commit'); + }, [issues]); + return ( {message}} @@ -87,24 +91,24 @@ const InputIssues = memo(() => { ) : ( <> - + setStep('commit')} + onSubmit={handleSubmit} options={options} /> {options.length === 0 && ( No issues found, press [Enter] to skip... )} - + ) ) : ( setStep('commit')} + onSubmit={handleSubmit} placeholder="Input number to link issues, press [Enter] to confirm or skip..." /> )} diff --git a/packages/lobe-commit/src/commands/Commit/InputIssuesType.tsx b/packages/lobe-commit/src/commands/Commit/InputIssuesType.tsx new file mode 100644 index 0000000..4151045 --- /dev/null +++ b/packages/lobe-commit/src/commands/Commit/InputIssuesType.tsx @@ -0,0 +1,54 @@ +import { Panel, SelectInput, type SelectInputProps } from '@lobehub/cli-ui'; +import { Text, useInput } from 'ink'; +import { memo, useCallback } from 'react'; +import { shallow } from 'zustand/shallow'; + +import { useCommitStore } from '@/store/commitStore'; + +import Header from './Header'; + +const items: SelectInputProps['items'] = [ + { + label: 'Only link issues', + value: '', + }, + { + label: 'close #X', + value: 'close', + }, + { + label: 'fix #X', + value: 'fix', + }, + { + label: 'resolve #X', + value: 'resolve', + }, +]; + +const InputIssues = memo(() => { + const { message, setIssuesType, setStep } = useCommitStore( + (st) => ({ + message: st.message, + setIssuesType: st.setIssuesType, + setStep: st.setStep, + }), + shallow, + ); + useInput(useCallback((_, key) => key.tab && setStep('issues'), [])); + + return ( + {message}} + header={
} + > + setIssuesType(item.value as any)} + onSelect={() => setStep('commit')} + /> + + ); +}); + +export default InputIssues; diff --git a/packages/lobe-commit/src/commands/Commit/InputScope.tsx b/packages/lobe-commit/src/commands/Commit/InputScope.tsx index 218dfaf..44989a5 100644 --- a/packages/lobe-commit/src/commands/Commit/InputScope.tsx +++ b/packages/lobe-commit/src/commands/Commit/InputScope.tsx @@ -1,14 +1,78 @@ import { TextInput } from '@inkjs/ui'; -import { Panel } from '@lobehub/cli-ui'; +import { Panel, SelectInput, SelectInputItem, SplitView } from '@lobehub/cli-ui'; import { Text, useInput } from 'ink'; import { debounce } from 'lodash-es'; -import { memo, useCallback } from 'react'; +import { memo, useCallback, useState } from 'react'; import { shallow } from 'zustand/shallow'; +import { ListItem } from '@/commands/List'; import { useCommitStore } from '@/store/commitStore'; import Header from './Header'; +const INPUT_VALUE = 'Use Input Value'; + +const commitScopes: SelectInputItem[] = [ + { + label: 'Input commit scope', + value: INPUT_VALUE, + }, + { + label: 'Package management changes, such as adding, updating, or removing dependencies', + value: 'deps', + }, + { + label: + 'Configuration file changes, such as adding, updating, or removing configuration options', + value: 'config', + }, + { + label: 'User interface changes, such as layout, style, or interaction modifications', + value: 'ui', + }, + { + label: 'API interface changes, such as adding, modifying, or removing API endpoints', + value: 'api', + }, + { + label: 'Database changes, such as adding, modifying, or removing tables, fields, or indexes', + value: 'database', + }, + { + label: 'Data model changes, such as adding, modifying, or removing data models', + value: 'model', + }, + { + label: 'Controller changes, such as adding, modifying, or removing controllers', + value: 'controller', + }, + { + label: 'View changes, such as adding, modifying, or removing views', + value: 'view', + }, + { + label: 'Route changes, such as adding, modifying, or removing routes', + value: 'route', + }, + { + label: 'Test changes, such as adding, modifying, or removing test cases', + value: 'test', + }, +]; + +const items: SelectInputItem[] = commitScopes.map((scope) => ({ + label: ( + + ), + value: scope.value, +})); + const InputScope = memo(() => { const { message, setScope, setStep, scope } = useCommitStore( (st) => ({ @@ -20,6 +84,32 @@ const InputScope = memo(() => { shallow, ); useInput(useCallback((_, key) => key.tab && setStep('type'), [])); + const [isInput, setIsInput] = useState(true); + const [input, setInput] = useState(''); + + const handleSubmit = useCallback(() => { + setStep('subject'); + }, []); + + const handleInput = useCallback( + (e: string) => { + if (isInput) { + setScope(e); + setInput(e); + } + }, + [isInput], + ); + + const handleSelect = useCallback((e: SelectInputItem) => { + if (e.value === INPUT_VALUE) { + setIsInput(true); + setScope(input); + } else { + setIsInput(false); + setScope(e.value); + } + }, []); return ( { > setStep('subject')} - placeholder="Input commit , or press [Enter] to skip..." + onChange={debounce(handleInput, 100)} + onSubmit={handleSubmit} + placeholder="Input commit , or select below, press [Enter] to skip..." /> + + label} + items={items} + onHighlight={handleSelect} + onSelect={handleSubmit} + /> + ); }); diff --git a/packages/lobe-commit/src/commands/Commit/InputType.tsx b/packages/lobe-commit/src/commands/Commit/InputType.tsx index 0953cb5..bff62da 100644 --- a/packages/lobe-commit/src/commands/Commit/InputType.tsx +++ b/packages/lobe-commit/src/commands/Commit/InputType.tsx @@ -13,11 +13,13 @@ import Header from './Header'; const emojiFormatConfig = configStore.get(CONFIG_NAME.EMOJI_FORMAT) === 'emoji'; +const AI_VALUE = 'ai'; + const aiItem: SelectInputItem = { label: ( { }, [typeKeywords, type]); const handleSelect: SelectInputProps['onSelect'] = useCallback((e: SelectInputItem) => { - if (e.value === 'ai') { + if (e.value === AI_VALUE) { setStep('ai'); } else { const content = e.value.split(' ') as [string, string]; diff --git a/packages/lobe-commit/src/commands/Commit/index.tsx b/packages/lobe-commit/src/commands/Commit/index.tsx index d5700ca..48e1bb6 100644 --- a/packages/lobe-commit/src/commands/Commit/index.tsx +++ b/packages/lobe-commit/src/commands/Commit/index.tsx @@ -9,6 +9,7 @@ import getAbsoluteHooksPath from '@/utils/getAbsoluteHooksPath'; import AiCommit from './AiCommit'; import InputIssues from './InputIssues'; +import InputIssuesType from './InputIssuesType'; import InputScope from './InputScope'; import InputSubject from './InputSubject'; import InputType from './InputType'; @@ -34,12 +35,13 @@ const Commit = memo(({ hook }) => { ); } - if (step === 'commit') return ; - if (step === 'ai') return ; if (step === 'type') return ; if (step === 'scope') return ; if (step === 'subject') return ; if (step === 'issues') return ; + if (step === 'issuesType') return ; + if (step === 'ai') return ; + if (step === 'commit') return ; return; }); diff --git a/packages/lobe-commit/src/commands/List/index.tsx b/packages/lobe-commit/src/commands/List/index.tsx index c06dbd8..db5e40e 100644 --- a/packages/lobe-commit/src/commands/List/index.tsx +++ b/packages/lobe-commit/src/commands/List/index.tsx @@ -6,8 +6,8 @@ import gitmojis from '@/constants/gitmojis'; export interface ListItemProps { item: { - descEN: string; - emoji: string; + desc: string; + emoji?: string; name: string; type: string; }; @@ -19,10 +19,10 @@ export const ListItem = memo(({ item }) => { - {` ${item.emoji} ${item.type} `} + {` ${[item.emoji, item.type].filter(Boolean).join(' ')} `} - {`- ${item.descEN}`} + {`- ${item.desc}`} ); }); diff --git a/packages/lobe-commit/src/constants/gitmojis.ts b/packages/lobe-commit/src/constants/gitmojis.ts index 2a4555c..3aa983c 100644 --- a/packages/lobe-commit/src/constants/gitmojis.ts +++ b/packages/lobe-commit/src/constants/gitmojis.ts @@ -1,98 +1,72 @@ export default [ { code: ':sparkles:', - descCN: '引入新功能', - descEN: 'Introduce new features', + desc: 'Introduce new features', emoji: '✨', name: 'sparkles', type: 'feat', }, { code: ':bug:', - descCN: '修复 bug', - descEN: 'Fix a bug', + desc: 'Fix a bug', emoji: '🐛', name: 'bug', type: 'fix', }, { code: ':recycle:', - descCN: '重构代码', - descEN: 'Refactor code that neither fixes a bug nor adds a feature', + desc: 'Refactor code that neither fixes a bug nor adds a feature', emoji: '♻️', name: 'recycle', type: 'refactor', }, { code: ':zap:', - descCN: '提高性能', - descEN: 'A code change that improves performance', + desc: 'A code change that improves performance', emoji: '⚡', name: 'zap', type: 'perf', }, { code: ':lipstick:', - descCN: '添加或更新 UI 和样式文件', - descEN: 'Add or update style files that do not affect the meaning of the code', + desc: 'Add or update style files that do not affect the meaning of the code', emoji: '💄', name: 'lipstick', type: 'style', }, { code: ':white_check_mark:', - descCN: '添加、更新或通过测试', - descEN: 'Adding missing tests or correcting existing tests', + desc: 'Adding missing tests or correcting existing tests', emoji: '✅', name: 'white-check-mark', type: 'test', }, { code: ':memo:', - descCN: '添加或更新文档', - descEN: 'Documentation only changes', + desc: 'Documentation only changes', emoji: '📝', name: 'memo', type: 'docs', }, { code: ':construction_worker:', - descCN: '添加或更新 CI 构建系统', - descEN: 'Changes to our CI configuration files and scripts', + desc: 'Changes to our CI configuration files and scripts', emoji: '👷', name: 'construction-worker', type: 'ci', }, { code: ':wrench:', - descCN: '添加或更新配置文件', - descEN: 'Other changes that dont modify src or test file', + desc: 'Other changes that dont modify src or test file', emoji: '🔧', name: 'wrench', type: 'chore', }, { code: ':package:', - descCN: '打包更新', - descEN: 'Make architectural changes', + desc: 'Make architectural changes', emoji: '📦', name: 'package', type: 'build', }, - { - code: ':construction:', - descCN: '正在进行中', - descEN: 'Work in progress', - emoji: '🚧', - name: 'construction', - type: 'wip', - }, - { - code: ':boom:', - descCN: '引入破坏性更改', - descEN: 'Introduce breaking changes', - emoji: '💥', - name: 'boom', - type: 'BREAKING CHANGE', - }, ]; diff --git a/packages/lobe-commit/src/constants/template.ts b/packages/lobe-commit/src/constants/template.ts index ff6cf97..bba0972 100644 --- a/packages/lobe-commit/src/constants/template.ts +++ b/packages/lobe-commit/src/constants/template.ts @@ -1,7 +1,7 @@ import storeConfig, { CONFIG_NAME } from '@/constants/config'; import gitmojis from '@/constants/gitmojis'; -const typesExample = gitmojis.map((item) => `- ${item.type}: ${item.descEN}`).join('\n'); +const typesExample = gitmojis.map((item) => `- ${item.type}: ${item.desc}`).join('\n'); const custionPrompt: string | any = storeConfig.get(CONFIG_NAME.PROMPT); const locale: number | any = storeConfig.get(CONFIG_NAME.LOCALE); diff --git a/packages/lobe-commit/src/store/commitStore.ts b/packages/lobe-commit/src/store/commitStore.ts index b2cb7b0..83bdebf 100644 --- a/packages/lobe-commit/src/store/commitStore.ts +++ b/packages/lobe-commit/src/store/commitStore.ts @@ -1,23 +1,27 @@ import { kebabCase } from 'lodash-es'; import { create } from 'zustand'; -import genCommitMessage from '@/utils/genCommitMessage'; +import { type IssuesType, commitMessageToObj, commotObjToMessage } from '@/utils/genCommitMessage'; import getIssuesList from '@/utils/getIssuesList'; import getRepo from '@/utils/getRepo'; -export type Step = 'type' | 'scope' | 'subject' | 'issues' | 'ai' | 'commit'; +export type Step = 'type' | 'scope' | 'subject' | 'issues' | 'issuesType' | 'ai' | 'commit'; + export interface CommitStore { + body: string; emoji: string; fetchIssuesList: () => void; isGithubRepo: boolean; issueList: any[]; issues: string; issuesLoading: boolean; + issuesType: IssuesType; message: string; refreshMessage: () => void; scope: string; setEmoji: (emoji: string) => void; setIssues: (issues: string) => void; + setIssuesType: (type: IssuesType) => void; setMessage: (message: string) => void; setScope: (scope: string) => void; setStep: (step: Step) => void; @@ -28,6 +32,7 @@ export interface CommitStore { type: string; } export const useCommitStore = create((set, get) => ({ + body: '', emoji: '', fetchIssuesList: async () => { const data = await getRepo(); @@ -46,10 +51,11 @@ export const useCommitStore = create((set, get) => ({ issueList: [], issues: '', issuesLoading: false, + issuesType: '', message: '', refreshMessage: () => { - const { issues, scope, subject, type, emoji } = get(); - const message = genCommitMessage({ emoji, issues, scope, subject, type }); + const { issues, scope, subject, type, emoji, body, issuesType } = get(); + const message = commotObjToMessage({ body, emoji, issues, issuesType, scope, subject, type }); set({ message }); }, scope: '', @@ -61,8 +67,14 @@ export const useCommitStore = create((set, get) => ({ set({ issues }); get().refreshMessage(); }, + setIssuesType: (issuesType) => { + set({ issuesType }); + get().refreshMessage(); + }, setMessage: (message) => { - set({ message }); + const obj = commitMessageToObj(message); + set({ ...obj }); + get().refreshMessage(); }, setScope: (scope) => { set({ scope: kebabCase(scope) }); diff --git a/packages/lobe-commit/src/utils/genAiCommit.ts b/packages/lobe-commit/src/utils/genAiCommit.ts index bfccc3d..a842ec2 100644 --- a/packages/lobe-commit/src/utils/genAiCommit.ts +++ b/packages/lobe-commit/src/utils/genAiCommit.ts @@ -6,70 +6,91 @@ import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter'; import { execSync } from 'node:child_process'; import { CONFIG_NAME, default as storeConfig } from '@/constants/config'; -import gitmojis from '@/constants/gitmojis'; import template from '@/constants/template'; +import { addEmojiToMessage } from '@/utils/genCommitMessage'; const diffChunkSize: number | any = storeConfig.get(CONFIG_NAME.DIFF_CHUNK_SIZE) || 1000; // const timeout: number | any = storeConfig.get(CONFIG_NAME.TIMEOUT) || 10_000; const basePath: string | any = storeConfig.get(CONFIG_NAME.API_BASE_URL); const openAIApiKey: string | any = storeConfig.get(CONFIG_NAME.OPENAI_TOKEN); -const addEmoji = (message: string) => { - const [type, ...rest]: any = message.split(': '); - let emoji: string = '🔧'; - for (const item of gitmojis) { - if (type.includes(item.type)) emoji = item.emoji; - } - return `${emoji} ${type}: ${rest.join(': ')}`; -}; -export default async (setLoadingInfo: (text: string) => void) => { +const chat = new ChatOpenAI( + { + maxRetries: 10, + openAIApiKey, + temperature: 0.5, + }, + { + basePath, + }, +); + +interface GenAiCommitProps { + cacheSummary?: string; + setLoading: (state: boolean) => void; + setLoadingInfo: (text: string) => void; + setMessage: (text: string) => void; + setSummary: (text: string) => void; +} + +const genAiCommit = async ({ + setLoadingInfo, + setMessage, + setLoading, + setSummary, + cacheSummary, +}: GenAiCommitProps) => { + setLoadingInfo(' Generating...'); + setLoading(true); if (!openAIApiKey) throw new Error('🤯 Please set the OpenAI Token by lobe-commit --config'); - let diff = execSync('git diff --staged', { - maxBuffer: 1024 ** 6, - }).toString(); + let summary = cacheSummary; - if (!diff) throw new Error('🤯 No changes to commit'); + if (!summary) { + let diff = execSync('git diff --staged', { + maxBuffer: 1024 ** 6, + }).toString(); - const chat = new ChatOpenAI( - { - maxRetries: 10, - openAIApiKey, - temperature: 0.5, - }, - { - basePath, - }, - ); - const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: diffChunkSize }); - const diffDocument = await textSplitter.createDocuments([diff]); - let summary = diff; - if (diffDocument.length > 1) { + // STEP 1 + if (!diff) throw new Error('🤯 No changes to commit'); + const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: diffChunkSize }); + const diffDocument = await textSplitter.createDocuments([diff]); + summary = diff; setLoadingInfo( ` [1/3] Split diff info to (${diffDocument.length}) by ${diffChunkSize} chunk size...`, ); + + // STEP 2 const chain = loadSummarizationChain(chat, { type: 'map_reduce' }); setLoadingInfo( ` [2/3] Split diff info to (${diffDocument.length} * ${diffChunkSize} chunk-size), generate summary...`, ); + + // STEP 3 const diffSummary = await chain.call({ input_documents: diffDocument, }); if (!diffSummary['text']) throw new Error('🤯 Diff summary failed'); - summary = diffSummary['text']; + summary = String(diffSummary['text']); + setSummary(summary); + setLoadingInfo(` [3/3] Generate commit message...`); } - setLoadingInfo(` [3/3] Generate commit message...`); + // STEP 4 const res = await chat.call([ new SystemMessage(template), new HumanMessage( `Return only 1 type commit message describes the git diff summary: ${summary}`, ), ]); - if (!res['text']) throw new Error('🤯 Diff summary failed'); - return addEmoji( + const message = addEmojiToMessage( res['text'].replace(/\((.*?)\):/, (match, p1) => match && `(${p1.toLowerCase()}):`), ); + + setMessage(message); + setLoading(false); }; + +export default genAiCommit; diff --git a/packages/lobe-commit/src/utils/genCommitMessage.ts b/packages/lobe-commit/src/utils/genCommitMessage.ts index 694eefd..0a9db42 100644 --- a/packages/lobe-commit/src/utils/genCommitMessage.ts +++ b/packages/lobe-commit/src/utils/genCommitMessage.ts @@ -1,31 +1,68 @@ +import { kebabCase, upperFirst } from 'lodash-es'; import pangu from 'pangu'; -export default ({ - emoji, - type, - scope, - subject, - issues, -}: { +import gitmojis from '@/constants/gitmojis'; + +export type IssuesType = '' | 'fix' | 'close' | 'resove'; +export interface CommitMessageObj { + body?: string; emoji?: string; issues?: string; + issuesType?: IssuesType; scope?: string; subject: string; type: string; -}): string => { +} + +export const commitMessageToObj = (msg: string): CommitMessageObj => { + const emojiReg = /^(\S+)\s/; + const typeReg = /\s(\w+)(?=\(|:)/; + const scopeReg = /\(([^)]+)\)/; + const subjectReg = /:\s([^\n:]+)/s; + + return { + body: msg.indexOf('\n') > 0 ? msg.slice(Math.max(0, msg.indexOf('\n') + 1)).trim() : undefined, + emoji: emojiReg.test(msg) ? msg.match(emojiReg)?.[1] || '🔧' : '🔧', + scope: scopeReg.test(msg) ? msg.match(scopeReg)?.[1] : undefined, + subject: subjectReg.test(msg) ? msg.match(subjectReg)?.[1] || 'Nothing' : 'Nothing', + type: typeReg.test(msg) ? msg.match(typeReg)?.[1] || 'chore' : 'chore', + }; +}; + +export const commotObjToMessage = ({ + emoji, + type, + scope, + subject, + issues, + body, + issuesType, +}: CommitMessageObj): string => { if (!type) return 'waiting for selection...'; - let message = [emoji, type].filter(Boolean).join(' '); - if (scope) message = `${message}(${scope.toLowerCase()})`; - message = `${message}: ${pangu.spacing(subject)}`; - if (issues) { - const issuesGroup = issues + + const formateType = type.toLowerCase(); + const formateScope = scope && kebabCase(scope).replaceAll(/\s+/g, ' '); + const formateSubject = upperFirst(pangu.spacing(subject).replaceAll(/\s+/g, ' ')); + const formateIssues = + issues && + issues .replace('#', '') .replaceAll(/\s+/g, ' ') .replaceAll(/[ ./|,]/g, ',') .split(',') .filter(Boolean) - .map((index) => '#' + index); - message = `${message} [${issuesGroup.join(',')}]`; + .map((num) => `${issuesType ? `${issuesType} ` : ''}#${num}`); + + return `${emoji} ${formateType}${formateScope ? `(${formateScope})` : ''}: ${formateSubject}${ + formateIssues && formateIssues?.length > 0 ? ` [${formateIssues.join(',')}]` : '' + }${body ? `\n\n${body}` : ''}`; +}; + +export const addEmojiToMessage = (message: string) => { + const [type, ...rest]: any = message.split(': '); + let emoji: string = '🔧'; + for (const item of gitmojis) { + if (type.includes(item.type)) emoji = item.emoji; } - return message.replaceAll(/\s+/g, ' '); + return `${emoji} ${type}: ${rest.join(': ')}`; };