Skip to content
Merged
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
build/bin
node_modules
frontend/dist
frontend/dist

green-wall.exe
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,39 @@ Make sure Git is installed on your computer.

Download the app, open it, and first grab a Personal Access Token (PAT) so you can sign in to GitHub. You can follow this guide: [how to get your PAT](docs/githubtoken_en.md).

### New: Image → Contribution Heatmap

- Upload any image (PNG/JPG/SVG) and turn it into a contribution heatmap.
- Choose rows (1–7) and columns (1–52) to fit your image’s shape.
- Pick a mode:
- **Auto** – works for most photos and grayscale images.
- **Binary** – best for black‑on‑white text or line art.
- Fine‑tune with brightness invert, threshold, scaling filter, and stroke recovery.
- Hover on the calendar to place the pattern, click to apply, right‑click to cancel.

#### Quick guide

| # | What you have | Recommended settings |
|---|---------------|----------------------|
| 1 | Black text on white background | Mode: Binary; Invert: on; Smoothing: Nearest; Threshold: 0–10; Recovery 1: 12–24; Dilation: 0–1 |
| 2 | Low‑contrast photo or drawing | Mode: Auto; Smoothing: Bilinear; Threshold: 0–15 |
| 3 | Very thin lines (≤2px) | Mode: Binary; Invert: on; Smoothing: Nearest; Recovery 1: 20–28; Dilation: 1–2 |
| 4 | Bright/noisy photo | Mode: Auto; Smoothing: Bilinear; Threshold: 5–20 |

**Tips**
- Use “Target rows” to keep tall images from being flattened.
- If Binary mode looks too empty, increase “Binary stroke recovery” or add “Stroke dilation”.
- For sharp text, keep “Scaling filter” on “Nearest (preserve sharp edges)”.

**Examples**
- ![success_example1](docs/images/success_example1.png) – Black text, Binary mode
- ![success_example2](docs/images/success_example2.png) – Grayscale photo, Auto mode
- ![success_example3](docs/images/success_example3.png) – Thin line art, Binary + dilation
- ![success_example4](docs/images/success_example4.png) – Balanced photo, Auto mode
- ![failure_example1](docs/images/failure_example1.png) – Over‑exposed photo (try higher threshold or invert off)
- ![failure_example2](docs/images/failure_example2.png) – Low contrast (increase threshold or use Binary + recovery)
- ![failure_example3](docs/images/failure_example3.png) – Noisy background (crop or smooth, then raise threshold)

Once you’re logged in you’ll see your avatar and name in the upper-left corner. Drag across the calendar to paint your design. When you’re satisfied, click **Create Remote Repo**. You can edit the repo name and description, choose whether it’s public or private, and then press **Generate & Push** to let the app create and push the repository for you automatically.

> **Heads-up:** GitHub may take anywhere from 5 minutes to 2 days to show the contributions on your profile. You can keep the repo private and enable “Include private contributions” in your profile settings so others can’t see the repo content but the contribution streak still counts.
Expand Down
26 changes: 26 additions & 0 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,32 @@

下载软件,打开后,首先要获取你的PAT来登录github,你可以参考这个:[如何获取你的github访问令牌](docs/githubtoken.md)

### 新增:图片转贡献图

- 上传图片(PNG/JPG/SVG),一键生成贡献热力图。
- 尽量选择线条清晰、对比度的照片,但是细节不宜过多,否则将被压缩的难以辨认。
- 自由设置行数(1~7)和列数(1~52),匹配图片形状。
- 两种模式:
- **自动** – 出来会有色阶变化
- **二值化** – 出来是最深和无色
- 可调亮度反转、阈值、缩放平滑、笔画补强。
- 在日历上悬停定位,左键应用,右键取消。

**小贴士**
- 二值化结果太稀疏时,加大“二值补笔画强度”
- 文字清晰优先选“邻近点(保细节)”。
- 又想要色阶变化,又想要文字清晰,可以适当调高亮度阈值

**成果示例**
![success_example1](docs/images/success_example1.png)
![success_example2](docs/images/success_example2.png)
![success_example3](docs/images/success_example3.png)
![success_example4](docs/images/success_example4.png)
**失败示例**
![failure_example1](docs/images/failure_example1.png)
![failure_example2](docs/images/failure_example2.png)
![failure_example3](docs/images/failure_example3.png)

登录成功左上角会显示你的头像和名字。拖动鼠标在日历上尽情画画,发挥你的艺术才能!画完后点击创建远程仓库,你可以自定义仓库名称和描述,选择仓库是否公开,确认无误后点击生成并且推送,软件会自动在你的GitHub上创建对应的仓库。

注意: GitHub 可能需要 5 分钟至两天才会显示你的贡献度图案。你可以把仓库设置为私人仓库,并在贡献统计中允许显示私人仓库的贡献,这样他人看不到仓库内容但可以看到贡献记录。
Expand Down
Binary file added docs/images/failure_example1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/failure_example2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/failure_example3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/success_example1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/success_example2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/success_example3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/success_example4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json.md5
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cf7eeb812cadce6dc9108a9563da0e8c
c23a00f6832717449a7945163092849e
77 changes: 61 additions & 16 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import GitPathSettings from './components/GitPathSettings';
import LoginModal from './components/LoginModal';
import { TranslationProvider, useTranslations, Language } from './i18n';
import { BrowserOpenURL, EventsOn } from '../wailsjs/runtime/runtime';
import type { main } from '../wailsjs/go/models';

function App() {
Expand Down Expand Up @@ -48,6 +47,12 @@
};

const AppLayout: React.FC<AppLayoutProps> = ({ contributions }) => {
const hasWailsApp = React.useCallback(() => {
if (typeof window === 'undefined') return false;
const w = window as typeof window & { go?: { main?: { App?: unknown } } };
return Boolean(w?.go?.main?.App);
}, []);

const { language, setLanguage, t } = useTranslations();
const [isGitInstalled, setIsGitInstalled] = React.useState<boolean | null>(null);
const [isGitPathSettingsOpen, setIsGitPathSettingsOpen] = React.useState<boolean>(false);
Expand All @@ -56,14 +61,22 @@

const checkGit = React.useCallback(async () => {
try {
const { CheckGitInstalled } = await import('../wailsjs/go/main/App');
const response = await CheckGitInstalled();
if (typeof window !== 'undefined' && !hasWailsApp()) {
console.warn('CheckGitInstalled skipped: wails runtime not available (dev mode)');
setIsGitInstalled(false);
return;
}
const mod = await import('../wailsjs/go/main/App');
if (!mod || typeof mod.CheckGitInstalled !== 'function') {
throw new Error('CheckGitInstalled not available');
}
const response = await mod.CheckGitInstalled();
setIsGitInstalled(response.installed);
} catch (error) {
console.error('Failed to check Git installation:', error);
setIsGitInstalled(false);
}
}, []);

Check warning on line 79 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Linux, green-wall)

React Hook React.useCallback has a missing dependency: 'hasWailsApp'. Either include it or remove the dependency array

Check warning on line 79 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / build (windows-latest, Windows, green-wall.exe)

React Hook React.useCallback has a missing dependency: 'hasWailsApp'. Either include it or remove the dependency array

Check warning on line 79 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / build (macos-latest, macOS, green-wall)

React Hook React.useCallback has a missing dependency: 'hasWailsApp'. Either include it or remove the dependency array

React.useEffect(() => {
checkGit();
Expand All @@ -72,8 +85,15 @@
React.useEffect(() => {
(async () => {
try {
const { GetGithubLoginStatus } = await import('../wailsjs/go/main/App');
const status = await GetGithubLoginStatus();
if (typeof window !== 'undefined' && !hasWailsApp()) {
console.warn('GetGithubLoginStatus skipped: wails runtime not available (dev mode)');
return;
}
const mod = await import('../wailsjs/go/main/App');
if (!mod || typeof mod.GetGithubLoginStatus !== 'function') {
throw new Error('GetGithubLoginStatus not available');
}
const status = await mod.GetGithubLoginStatus();
if (status.authenticated && status.user) {
setGithubUser(status.user);
} else {
Expand All @@ -83,19 +103,31 @@
console.error('Failed to fetch GitHub login status:', error);
}
})();
}, []);

Check warning on line 106 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, Linux, green-wall)

React Hook React.useEffect has a missing dependency: 'hasWailsApp'. Either include it or remove the dependency array

Check warning on line 106 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / build (windows-latest, Windows, green-wall.exe)

React Hook React.useEffect has a missing dependency: 'hasWailsApp'. Either include it or remove the dependency array

Check warning on line 106 in frontend/src/App.tsx

View workflow job for this annotation

GitHub Actions / build (macos-latest, macOS, green-wall)

React Hook React.useEffect has a missing dependency: 'hasWailsApp'. Either include it or remove the dependency array

React.useEffect(() => {
const unsubscribe = EventsOn('github:auth-changed', (status: main.GithubLoginStatus) => {
if (status && status.authenticated && status.user) {
setGithubUser(status.user);
return;
let unsubscribe: (() => void) | undefined;
(async () => {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Async dynamic import in the effect can register EventsOn after unmount, leaving an uncleaned listener and possible state updates on an unmounted component.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/App.tsx, line 110:

<comment>Async dynamic import in the effect can register `EventsOn` after unmount, leaving an uncleaned listener and possible state updates on an unmounted component.</comment>

<file context>
@@ -86,16 +106,28 @@ const AppLayout: React.FC<AppLayoutProps> = ({ contributions }) => {
-        setGithubUser(status.user);
-        return;
+    let unsubscribe: (() => void) | undefined;
+    (async () => {
+      try {
+        const { EventsOn } = await import('../wailsjs/runtime/runtime');
</file context>
Fix with Cubic

try {
const { EventsOn } = await import('../wailsjs/runtime/runtime');
if (typeof EventsOn === 'function') {
unsubscribe = EventsOn('github:auth-changed', (status: main.GithubLoginStatus) => {
if (status && status.authenticated && status.user) {
setGithubUser(status.user);
return;
}
setGithubUser(null);
});
}
} catch (error) {
console.warn('EventsOn not available (dev mode)', error);
}
setGithubUser(null);
});
})();

return () => {
unsubscribe();
if (unsubscribe) {
unsubscribe();
}
};
}, []);

Expand All @@ -115,8 +147,10 @@
const logoutLabel = language === 'zh' ? '退出' : 'Log out';
const handleLogout = React.useCallback(async () => {
try {
const { LogoutGithub } = await import('../wailsjs/go/main/App');
await LogoutGithub();
const mod = await import('../wailsjs/go/main/App');
if (typeof mod?.LogoutGithub === 'function') {
await mod.LogoutGithub();
}
setGithubUser(null);
} catch (error) {
console.error('Failed to log out from GitHub:', error);
Expand All @@ -127,8 +161,19 @@
}, []);
const displayName = githubUser?.name?.trim() || githubUser?.login || '';

const openRepository = React.useCallback(() => {
BrowserOpenURL('https://github.com/zmrlft/GreenWall');
const openRepository = React.useCallback(async () => {
try {
const { BrowserOpenURL } = await import('../wailsjs/runtime/runtime');
if (typeof BrowserOpenURL === 'function') {
BrowserOpenURL('https://github.com/zmrlft/GreenWall');
return;
}
} catch (error) {
console.warn('BrowserOpenURL not available (dev mode)', error);
}
if (typeof window !== 'undefined') {
window.open('https://github.com/zmrlft/GreenWall', '_blank', 'noopener,noreferrer');
}
}, []);

return (
Expand Down
Loading