Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 39 additions & 19 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,32 +1,52 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
# Wails
build/
tmp/
*.db
*.dylib
*.dll
.wails/

# Go
vendor/
*.exe
*.exe~
*.dll
*.so
*.dylib
go.work
go.work.sum

# Test binary, built with `go test -c`
# Test binary
*.test

# Code coverage profiles and other test artifacts
*.out
coverage.*
*.coverprofile
profile.cov

# Dependency directories (remove the comment below to include it)
# vendor/
# Frontend
frontend/node_modules/
frontend/dist/
frontend/.env
frontend/.env.local
frontend/.env.*.local
frontend/coverage/

# Go workspace file
go.work
go.work.sum
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# env file
.env
# Editor
.idea/
.vscode/
*.swp
*.swo
*~
.DS_Store

# OS
Thumbs.db

# Editor/IDE
# .idea/
# .vscode/
# Environment
.env
.env.local
208 changes: 208 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package main

import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/wailsapp/wails/v2/pkg/runtime"
)

type App struct {
ctx context.Context
dataDir string
}

type CommitTemplate struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Scope string `json:"scope"`
Subject string `json:"subject"`
Body string `json:"body"`
Footer string `json:"footer"`
IsBreaking bool `json:"is_breaking"`
Description string `json:"description"`
}

type CommitType struct {
Value string `json:"value"`
Label string `json:"label"`
Description string `json:"description"`
}

var commitTypes = []CommitType{
{Value: "feat", Label: "feat", Description: "新功能"},
{Value: "fix", Label: "fix", Description: "修复Bug"},
{Value: "docs", Label: "docs", Description: "文档更新"},
{Value: "style", Label: "style", Description: "代码格式"},
{Value: "refactor", Label: "refactor", Description: "代码重构"},
{Value: "perf", Label: "perf", Description: "性能优化"},
{Value: "test", Label: "test", Description: "测试相关"},
{Value: "chore", Label: "chore", Description: "构建/工具"},
{Value: "revert", Label: "revert", Description: "回滚代码"},
{Value: "build", Label: "build", Description: "构建系统"},
{Value: "ci", Label: "ci", Description: "CI配置"},
}

func NewApp() *App {
return &App{}
}

func (a *App) startup(ctx context.Context) {
a.ctx = ctx

homeDir, err := os.UserHomeDir()
if err != nil {
runtime.LogError(ctx, fmt.Sprintf("无法获取用户目录: %v", err))
return
}

a.dataDir = filepath.Join(homeDir, ".gitcommit")
os.MkdirAll(a.dataDir, 0755)
}

func (a *App) shutdown(ctx context.Context) {
}

func (a *App) beforeClose(ctx context.Context) (prevent bool) {
return false
}

func (a *App) GetCommitTypes() []CommitType {
return commitTypes
}

func (a *App) GenerateCommitMessage(commitType, scope, subject, body, footer string, isBreaking bool) string {
var parts []string

if isBreaking {
parts = append(parts, commitType+"(!"+scope+")")
} else if scope != "" {
parts = append(parts, commitType+"("+scope+")")
} else {
parts = append(parts, commitType)
}

parts = append(parts, ": "+subject)

subjectLine := strings.Join(parts, "")

result := subjectLine

if body != "" {
result += "\n\n" + body
}

if footer != "" {
result += "\n\n" + footer
}

if isBreaking {
if !strings.Contains(result, "BREAKING CHANGE:") {
if footer != "" {
result += "\n\nBREAKING CHANGE: " + footer
} else {
result += "\n\nBREAKING CHANGE: " + subject
}
}
}

return result
}

func (a *App) GenerateGitCommand(commitMsg string) string {
escapedMsg := strings.ReplaceAll(commitMsg, "\"", "\\\"")
escapedMsg = strings.ReplaceAll(escapedMsg, "\n", "\\n")
return fmt.Sprintf("git commit -m \"%s\"", escapedMsg)
}

func (a *App) CopyToClipboard(text string) error {
runtime.ClipboardSetText(a.ctx, text)
return nil
}

func (a *App) GetClipboardText() string {
text, err := runtime.ClipboardGetText(a.ctx)
if err != nil {
return ""
}
return text
}

func (a *App) SaveTemplate(template CommitTemplate) error {
if template.ID == "" {
template.ID = fmt.Sprintf("template_%d", len(a.GetTemplates())+1)
}

templates := a.GetTemplates()

exists := false
for i, t := range templates {
if t.ID == template.ID {
templates[i] = template
exists = true
break
}
}

if !exists {
templates = append(templates, template)
}

return a.saveTemplatesToFile(templates)
}

func (a *App) GetTemplates() []CommitTemplate {
filePath := filepath.Join(a.dataDir, "templates.json")

data, err := os.ReadFile(filePath)
if err != nil {
return []CommitTemplate{}
}

var templates []CommitTemplate
err = json.Unmarshal(data, &templates)
if err != nil {
return []CommitTemplate{}
}

return templates
}

func (a *App) GetTemplate(id string) *CommitTemplate {
templates := a.GetTemplates()
for _, t := range templates {
if t.ID == id {
return &t
}
}
return nil
}

func (a *App) DeleteTemplate(id string) error {
templates := a.GetTemplates()
var newTemplates []CommitTemplate

for _, t := range templates {
if t.ID != id {
newTemplates = append(newTemplates, t)
}
}

return a.saveTemplatesToFile(newTemplates)
}

func (a *App) saveTemplatesToFile(templates []CommitTemplate) error {
filePath := filepath.Join(a.dataDir, "templates.json")

data, err := json.MarshalIndent(templates, "", " ")
if err != nil {
return err
}

return os.WriteFile(filePath, data, 0644)
}
13 changes: 13 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Git Commit Generator</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
Loading