Repository: haydenull/logseq-plugin-git
Branch: main
Commit: e0ae1958ab58
Files: 20
Total size: 31.6 KB
Directory structure:
gitextract_ftc14837/
├── .github/
│ └── workflows/
│ └── release.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── index.html
├── package.json
├── postcss.config.js
├── release.config.js
├── src/
│ ├── App.css
│ ├── App.tsx
│ ├── helper/
│ │ ├── constants.ts
│ │ ├── git.ts
│ │ └── util.ts
│ ├── index.css
│ ├── main.tsx
│ └── vite-env.d.ts
├── tailwind.config.js
├── tsconfig.json
└── vite.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
branches:
- main
jobs:
release:
name: Release
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm build
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release
================================================
FILE: .gitignore
================================================
node_modules
.DS_Store
dist
dist-ssr
*.local
================================================
FILE: CHANGELOG.md
================================================
# [1.7.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.6.0...v1.7.0) (2024-05-21)
### Features
* Added Custom Commit Message ([#60](https://github.com/haydenull/logseq-plugin-git/issues/60)) ([3773f8b](https://github.com/haydenull/logseq-plugin-git/commit/3773f8bbcf5a1004fa93d8af6c043c62a4104d9e))
# [1.6.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.5.5...v1.6.0) (2024-05-21)
### Bug Fixes
* :bug: release action error ([52d170b](https://github.com/haydenull/logseq-plugin-git/commit/52d170b88f4f1edb28810fe26a501978b0cfde6b))
### Features
* New keyboard shortcut to commit without pushing ([#59](https://github.com/haydenull/logseq-plugin-git/issues/59)) ([fc799b8](https://github.com/haydenull/logseq-plugin-git/commit/fc799b8a8a364487db5c44631d3982323343926d))
## [1.5.5](https://github.com/haydenull/logseq-plugin-git/compare/v1.5.4...v1.5.5) (2023-12-28)
### Bug Fixes
* patch git exec command ([#50](https://github.com/haydenull/logseq-plugin-git/issues/50)) ([b4a4229](https://github.com/haydenull/logseq-plugin-git/commit/b4a422930288ff32cf2ff2fd23a71f0ca862b606))
## [1.5.4](https://github.com/haydenull/logseq-plugin-git/compare/v1.5.3...v1.5.4) (2023-07-27)
### Bug Fixes
* exported inProcess() was not set correctly breaking checkIsSynced ([#37](https://github.com/haydenull/logseq-plugin-git/issues/37)) ([cf78518](https://github.com/haydenull/logseq-plugin-git/commit/cf785180e4f68e589a153b71056a2fe1163fade6))
## [1.5.3](https://github.com/haydenull/logseq-plugin-git/compare/v1.5.2...v1.5.3) (2023-07-26)
### Bug Fixes
* [#30](https://github.com/haydenull/logseq-plugin-git/issues/30), shortcut mod+alt+s will pull rebase ([5f42bd5](https://github.com/haydenull/logseq-plugin-git/commit/5f42bd5674cdcdff6ae60e789ddf9bc66223ed38))
* stack git operations not to get errors when auto actions are already performing ([e78bb2c](https://github.com/haydenull/logseq-plugin-git/commit/e78bb2c185787e731cb7b1af07487b6901389396)), closes [#34](https://github.com/haydenull/logseq-plugin-git/issues/34)
## [1.5.2](https://github.com/haydenull/logseq-plugin-git/compare/v1.5.1...v1.5.2) (2023-04-20)
### Bug Fixes
* :bug: the mask was not turned off after initilization ([a84acb5](https://github.com/haydenull/logseq-plugin-git/commit/a84acb5a7133da1f0693c15112ab5c903f0a1774))
## [1.5.1](https://github.com/haydenull/logseq-plugin-git/compare/v1.5.0...v1.5.1) (2023-04-16)
### Bug Fixes
* In the Logseq 0.9.2 version, there is an issue with the panel display that needs to be fixed. ([a81bceb](https://github.com/haydenull/logseq-plugin-git/commit/a81bceb10996a6e3d3901fe1f1fef63d6d1c4052))
# [1.5.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.4.2...v1.5.0) (2023-02-14)
### Features
* logseq sdk version ([#26](https://github.com/haydenull/logseq-plugin-git/issues/26)) ([d616d82](https://github.com/haydenull/logseq-plugin-git/commit/d616d820df800809cdc37e718fa0a43ff380498b))
## [1.4.2](https://github.com/haydenull/logseq-plugin-git/compare/v1.4.1...v1.4.2) (2022-11-06)
### Bug Fixes
* button color ([d3a5bd1](https://github.com/haydenull/logseq-plugin-git/commit/d3a5bd169ee74634983c04f160677a9f34f8971e))
## [1.4.1](https://github.com/haydenull/logseq-plugin-git/compare/v1.4.0...v1.4.1) (2022-11-01)
### Bug Fixes
* button style ([d32ef80](https://github.com/haydenull/logseq-plugin-git/commit/d32ef80c2105b3d7fb14da9b813c2eb29b7d3045))
# [1.4.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.3.0...v1.4.0) (2022-08-23)
### Features
* ✨ Support for turning off auto-check sync ([a2ac2bd](https://github.com/haydenull/logseq-plugin-git/commit/a2ac2bd44cd9685c15827bdb82bdec6c18139823))
# [1.3.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.2.0...v1.3.0) (2022-08-19)
### Features
* add default value: pull rebase ([90862ba](https://github.com/haydenull/logseq-plugin-git/commit/90862ba1d2d0d258c6d7b7a58f552fb0187f2df9))
# [1.2.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.1.0...v1.2.0) (2022-06-11)
### Features
* supports disable checkWhenDBChanged ([85428aa](https://github.com/haydenull/logseq-plugin-git/commit/85428aa217361a797f64c5eadd8a008e39cb49b2))
# [1.1.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.0.2...v1.1.0) (2022-05-22)
### Features
* ✨ add pull and checkout buttons ([f950626](https://github.com/haydenull/logseq-plugin-git/commit/f950626e2a3776c805a96267bbd37ba7a7eae9da))
## [1.0.2](https://github.com/haydenull/logseq-plugin-git/compare/v1.0.1...v1.0.2) (2022-05-20)
### Bug Fixes
* The id does not match expectations ([dffcaa3](https://github.com/haydenull/logseq-plugin-git/commit/dffcaa3b71086bad022350494c841bed7576d9c3))
## [1.0.1](https://github.com/haydenull/logseq-plugin-git/compare/v1.0.0...v1.0.1) (2022-05-05)
### Bug Fixes
* 🐛 Adapt logseq 0.6.7 ([02ffdf7](https://github.com/haydenull/logseq-plugin-git/commit/02ffdf70d493f0adc70d95d4847a7bb6ed6751b7))
* default value error[skip ci] ([a5cfc2b](https://github.com/haydenull/logseq-plugin-git/commit/a5cfc2b9184119f820946fcb0d33f5e5dc098e5d))
# 1.0.0 (2022-04-08)
### Features
* ✨ add icon ([0f52416](https://github.com/haydenull/logseq-plugin-git/commit/0f52416ef8594525fb2fb527bc05c98fa327e308))
# [1.1.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.0.0...v1.1.0) (2022-03-08)
### Features
* add antd ([036577d](https://github.com/haydenull/logseq-plugin-git/commit/036577dc529db4e4a5964c287a55d112bae654bc))
# 1.0.0 (2022-01-18)
### Features
* ✨ initial commit ([cca0e7f](https://github.com/haydenull/logseq-plugin-git/commit/cca0e7fcba33830eaf534fd9ca6b867b57147de4))
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2022 Hayden Chen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# logseq-plugin-git
> git plugin for logseq
[](https://github.com/haydenull/logseq-plugin-git/releases)
[](https://github.com/haydenull/logseq-plugin-git/blob/main/LICENSE)
Logseq Git Plugin is a plugin for Logseq that provides easy access to common git commands, and helps you keep your notes synchronized with a remote git repository.
With this plugin, you can:
- Know explicitly if there are unsaved notes in the current repository and if they are synchronized with the remote repository. When there are unsaved files locally, the plugin will show a red indicator in the toolbar icon.
- Quickly commit changes to notes. The plugin provides shortcut keys (mod+s) to perform commit&push operations.
- Provide buttons to quickly execute common commands. Click the plugin icon, and the drop-down menu will show you some buttons to help you quickly execute commands (you can specify the desired buttons in the settings).
- Prompt you if you have synchronized with the remote repository. The plugin automatically detects if the current repository is up-to-date and prompts when logseq starts and other times.
## Installation
Install from the Logseq Plugin Store
## Shortcuts
The plugin provides a shortcut key (mod+s) to quickly commit and push changes to the remote repository.
## Toolbar Icon
- icon: everything is up-to-date.
- icon with red dot: there are unsaved files locally.
- icon with blinking red dot: the plugin is committing or pushing changes to the remote repository.
## Commands
The drop-down menu shows the following commands:
- Check Status: check the status of the current repository.
- Show Log: show the log of the current repository.
- Pull: pull changes from the remote repository.
- Pull Rebase: pull changes from the remote repository and rebase.
- Commit: commit changes to the current repository.
- Push: push changes to the remote repository.
- Commit & Push: commit changes and push to the remote repository.
## Demo

================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
================================================
FILE: package.json
================================================
{
"name": "logseq-plugin-git",
"version": "1.7.0",
"main": "dist/index.html",
"logseq": {
"id": "logseq-git",
"icon": "logo.png"
},
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@logseq/libs": "^0.0.16",
"antd": "^4.18.9",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@semantic-release/npm": "^10.0.4",
"@types/node": "^20.4.5",
"@types/react": "^18.2.17",
"@types/react-dom": "^18.2.7",
"@vitejs/plugin-react": "^4.0.3",
"autoprefixer": "^10.4.2",
"postcss": "^8.4.27",
"semantic-release": "^21.1.2",
"tailwindcss": "^3.3.3",
"typescript": "^5.1.6",
"vite": "^4.4.7",
"vite-plugin-importer": "^0.2.5"
},
"packageManager": "pnpm@8.6.10"
}
================================================
FILE: postcss.config.js
================================================
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
================================================
FILE: release.config.js
================================================
const pluginName = require('./package.json').name
module.exports = {
branches: 'main',
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
'@semantic-release/changelog',
[
'@semantic-release/npm',
{ npmPublish: false }
],
[
'@semantic-release/git',
{
assets: ['CHANGELOG.md', 'package.json'],
message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}'
}
],
[
'@semantic-release/exec',
{
prepareCmd: 'zip -qq -r ' + pluginName + '-${nextRelease.version}.zip package.json logo.png dist/'
}
],
[
'@semantic-release/github',
{
assets: [`${pluginName}-*.zip`]
}
]
]
}
================================================
FILE: src/App.css
================================================
================================================
FILE: src/App.tsx
================================================
import './App.css'
const App: React.FC<{ env: string }> = ({ env }) => {
return (
<div className="w-screen h-screen flex items-center justify-center text-white">
<div className="w-screen h-screen fixed top-0 left-0" onClick={() => logseq.hideMainUI()}></div>
<div className="w-5/6 h-5/6 bg-gradient-to-tr from-yellow-400 via-red-500 to-pink-500 flex flex-col items-center justify-center">
<h1 className="font-bold text-4xl">logseq-plugin-git</h1>
<h2 className="text-2xl mt-6">Current Env: {env}</h2>
</div>
</div>
)
}
export default App
================================================
FILE: src/helper/constants.ts
================================================
import { SettingSchemaDesc } from "@logseq/libs/dist/LSPlugin.user";
export const COMMON_STYLE = `
.ui-items-container[data-type=toolbar] > .list-wrap {
overflow: visible;
}
#injected-ui-item-git-logseq-git {
position: relative;
}
.plugin-git-container {
display: none;
}
.plugin-git-container .plugin-git-mask {
position: fixed;
width: 100vw;
height: 100vh;
left: 0;
top: 0;
z-index: 99;
}
.plugin-git-container .plugin-git-popup {
position: fixed;
z-index: 99;
background-color: var(--ls-secondary-background-color);
padding: 10px;
border-radius: .375rem;
--tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),0 4px 6px -2px rgba(0, 0, 0, 0.05);
box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);
}
.plugin-git-container .plugin-git-popup::before {
content: '';
position: absolute;
top: -8px;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 0;
border-style: solid;
border-width: 0 10px 8px 10px;
border-color: transparent transparent var(--ls-secondary-background-color) transparent;
}
`;
export const SHOW_POPUP_STYLE = `
.plugin-git-container {
display: block;
}
`;
export const HIDE_POPUP_STYLE = `
.plugin-git-container {
display: none;
}
`;
export const INACTIVE_STYLE = `
${COMMON_STYLE}
#injected-ui-item-git-logseq-git::after {
display: none;
}
`;
export const ACTIVE_STYLE = `
${COMMON_STYLE}
#injected-ui-item-git-logseq-git::after {
display: block;
content: '';
position: absolute;
width: 8px;
height: 8px;
border-radius: 100%;
background-color: rgb(237, 66, 69);
right: 8px;
top: 6px;
}
`;
export const LOADING_STYLE = `
${COMMON_STYLE}
#injected-ui-item-git-logseq-git::after {
display: block;
content: '';
position: absolute;
width: 8px;
height: 8px;
border-radius: 100%;
background-color: rgb(237, 66, 69);
right: 8px;
top: 6px;
animation: blink 1s linear infinite;
}
@keyframes blink {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
`;
export const BUTTONS = [
{ key: "status", title: "Check Status", event: "check" },
{ key: "log", title: "Show Log", event: "log" },
{ key: "pull", title: "Pull", event: "pull" },
{ key: "pullRebase", title: "Pull Rebase", event: "pullRebase" },
{ key: "checkout", title: "Checkout", event: "checkout" },
{ key: "commit", title: "Commit", event: "commit" },
{ key: "push", title: "Push", event: "push" },
{ key: "commitAndPush", title: "Commit & Push", event: "commitAndPush" },
];
export const SETTINGS_SCHEMA: SettingSchemaDesc[] = [
{
key: "buttons",
title: "Buttons",
type: "enum",
default: ["Check Status", "Show Log", "Pull Rebase", "Commit & Push"],
description: "Select buttons to show",
enumPicker: "checkbox",
enumChoices: BUTTONS.map(({ title }) => title),
},
{
key: "checkWhenDBChanged",
title: "Check Status when DB Changed",
type: "boolean",
default: true,
description: "Check status when DB changed, restart logseq to take effect",
},
{
key: "autoCheckSynced",
title: "Auto Check If Synced",
type: "boolean",
default: false,
description:
"Automatically check if the local version is the same as the remote",
},
{
key: "autoPush",
title: "Auto Push",
type: "boolean",
default: false,
description: "Auto push when logseq hide",
},
{
key: "typeCommitMessage",
title: "Type Commit Message",
type: "enum",
default: "Default Message With Date",
description: "Type of commit message to use",
enumPicker: "select",
enumChoices: ['Custom Message' , 'Default Message', 'Custom Message With Date', 'Default Message With Date'],
},
{
key: "customCommitMessage",
title: "Custom Commit Message",
type: "string",
default: "",
description: "Custom commit message for plugin (valid only if commit message is set to Custom Message)",
}
];
================================================
FILE: src/helper/git.ts
================================================
// https://logseq.github.io/plugins/interfaces/IAppProxy.html#execGitCommand
import type { IGitResult } from "@logseq/libs/dist/LSPlugin.user"
let _inProgress: Promise<IGitResult> | undefined = undefined
export const execGitCommand = async (args: string[]) : Promise<IGitResult> => {
if (_inProgress) await _inProgress
let res
try {
const currentGitFolder = (await logseq.App.getCurrentGraph())?.path
const runArgs = currentGitFolder ? ['-C', currentGitFolder, ...args] : args
_inProgress = logseq.Git.execCommand(runArgs)
res = await _inProgress
} finally {
_inProgress = undefined
}
return res
}
export const inProgress = () => _inProgress
export const status = async (showRes = true): Promise<IGitResult> => {
// git status --porcelain | awk '{print $2}'
// git status --porcelain | wc -l
const res = await execGitCommand(['status', '--porcelain'])
console.log('[faiz:] === git status', res)
if (showRes) {
if (res.exitCode === 0) {
logseq.UI.showMsg('Git status success')
} else {
logseq.UI.showMsg(`Git status failed\n${res.stderr}`, 'error')
}
}
/**
* res
* modify
* {
* exitCode: 0,
* stderr: '',
* stdout: 'M foo.md\n?? bar.md\n',
* }
* ahead & uptodate & behind
* {
* exitCode: 0,
* stderr: '',
* stdout: '',
* }
*/
// changed files staged files
return res
}
// log with git log --pretty=format:"%h %ad | %s%d [%an]" --date=short
export const log = async (showRes = true): Promise<IGitResult> => {
// git log --pretty=format:"%h %s" -n 1
// git log --pretty=format:"%h %ad | %s%d [%an]" --date=short
// return await logseq.App.execGitCommand(['log', '--pretty=format:"%h %s"'])
const res = await execGitCommand(['log', '--pretty=format:"%h %ad | %s [%an]"', '--date=format:"%Y-%m-%d %H:%M:%S"', '--name-status'])
console.log('[faiz:] === git log', res)
if (showRes) {
if (res.exitCode === 0) {
logseq.UI.showMsg('Git log success')
} else {
logseq.UI.showMsg(`Git log failed\n${res.stderr}`, 'error')
}
}
return res
}
// git pull
export const pull = async (showRes = true): Promise<IGitResult> => {
const res = await execGitCommand(['pull'])
console.log('[faiz:] === git pull', res)
if (showRes) {
if (res.exitCode === 0) {
logseq.UI.showMsg('Git pull success')
} else {
logseq.UI.showMsg(`Git pull failed\n${res.stderr}`, 'error')
}
}
return res
}
// git pull --rebase
export const pullRebase = async (showRes = true): Promise<IGitResult> => {
const res = await execGitCommand(['pull', '--rebase'])
console.log('[faiz:] === git pull --rebase', res)
if (showRes) {
if (res.exitCode === 0) {
logseq.UI.showMsg('Git pull --rebase success')
} else {
logseq.UI.showMsg(`Git pull --rebase failed\n${res.stderr}`, 'error')
}
}
return res
}
// git checkout .
export const checkout = async (showRes = true): Promise<IGitResult> => {
const res = await execGitCommand(['checkout', '.'])
console.log('[faiz:] === git checkout .', res)
if (showRes) {
if (res.exitCode === 0) {
logseq.UI.showMsg('Git checkout success')
} else {
logseq.UI.showMsg(`Git checkout failed\n${res.stderr}`, 'error')
}
}
return res
}
// git commit
export const commit = async (showRes = true, message: string): Promise<IGitResult> => {
await execGitCommand(['add', '.'])
// git commit -m "message"
const res = await execGitCommand(['commit', '-m', message])
console.log('[faiz:] === git commit', res)
if (showRes) {
if (res.exitCode === 0) {
logseq.UI.showMsg('Git commit success')
} else {
logseq.UI.showMsg(`Git commit failed\n${res.stdout || res.stderr}`, 'error')
}
}
return res
}
// push
export const push = async (showRes = true): Promise<IGitResult> => {
// git push
const res = await execGitCommand(['push'])
console.log('[faiz:] === git push', res)
if (showRes) {
if (res.exitCode === 0) {
logseq.UI.showMsg('Git push success')
} else {
logseq.UI.showMsg(`Git push failed\n${res.stderr}`, 'error')
}
}
return res
}
/**
* Returns the commit message based on the selected commit message type in the logseq settings.
* @returns The commit message.
*/
export const commitMessage = () : string => {
let defaultMessage = "[logseq-plugin-git:commit]";
switch (logseq.settings?.typeCommitMessage as string) {
case "Default Message":
return defaultMessage;
case "Default Message With Date":
return defaultMessage + " " + new Date().toISOString();
case "Custom Message":
let customMessage = logseq.settings?.customCommitMessage as string;
if (customMessage.trim() === "") {
return defaultMessage;
} else {
return customMessage;
}
case "Custom Message With Date":
let customMessageWithDate = logseq.settings?.customCommitMessage as string;
if (customMessageWithDate.trim() === "") {
return defaultMessage + " " + new Date().toISOString();
} else {
return customMessageWithDate + " " + new Date().toISOString();
}
default:
return defaultMessage;
}
}
================================================
FILE: src/helper/util.ts
================================================
import {
ACTIVE_STYLE,
HIDE_POPUP_STYLE,
INACTIVE_STYLE,
SHOW_POPUP_STYLE,
} from "./constants";
import { status, inProgress, execGitCommand } from "./git";
export const checkStatus = async () => {
console.log("Checking status...");
const statusRes = await status(false);
if (statusRes?.stdout === "") {
console.log("No changes", statusRes);
setPluginStyle(INACTIVE_STYLE);
} else {
console.log("Need save", statusRes);
setPluginStyle(ACTIVE_STYLE);
}
return statusRes;
};
let pluginStyle = "";
export const setPluginStyle = (style: string) => {
pluginStyle = style;
logseq.provideStyle({ key: "git", style });
};
export const getPluginStyle = () => pluginStyle;
export const showPopup = () => {
const _style = getPluginStyle();
logseq.UI.queryElementRect("#logseq-git--git").then((triggerIconRect) => {
console.log("[faiz:] === triggerIconRect", triggerIconRect);
if (!triggerIconRect) return;
const popupWidth = 120 + 10 * 2;
const left =
triggerIconRect.left + triggerIconRect.width / 2 - popupWidth / 2;
const top = triggerIconRect.top + triggerIconRect.height;
const _style = getPluginStyle();
setPluginStyle(
`${_style}\n.plugin-git-popup{left:${left}px;top:${top}px;}`
);
});
setPluginStyle(`${_style}\n${SHOW_POPUP_STYLE}`);
};
export const hidePopup = () => {
const _style = getPluginStyle();
setPluginStyle(`${_style}\n${HIDE_POPUP_STYLE}`);
};
export const debounce = (fn, wait: number = 100, environment?: any) => {
let timer = null;
return function () {
// @ts-ignore
const context = environment || this;
const args = arguments;
if (timer) {
clearTimeout(timer);
timer = null;
}
// @ts-ignore
timer = setTimeout(function () {
fn.apply(context, args);
}, wait);
};
};
export const checkStatusWithDebounce = debounce(() => {
checkStatus();
}, 2000);
export const isRepoUpTodate = async () => {
await execGitCommand(["fetch"]);
const local = await execGitCommand(["rev-parse", "HEAD"]);
const remote = await execGitCommand(["rev-parse", "@{u}"]);
logseq.UI.showMsg(`${local.stdout} === ${remote.stdout}`, "success", { timeout: 30 });
return local.stdout === remote.stdout;
};
export const checkIsSynced = async () => {
if (inProgress()) {
console.log("[faiz:] === checkIsSynced Git in progress, skip check");
return
}
const isSynced = await isRepoUpTodate();
if (!isSynced)
logseq.UI.showMsg(
`The current repository is not synchronized with the remote repository, please check.`,
"warning",
{ timeout: 0 }
);
};
================================================
FILE: src/index.css
================================================
@tailwind base;
@tailwind components;
@tailwind utilities;
html, body {
background-color: transparent;
}
#root {
@apply w-screen h-screen;
}
================================================
FILE: src/main.tsx
================================================
import "@logseq/libs";
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { BUTTONS, LOADING_STYLE, SETTINGS_SCHEMA } from "./helper/constants";
import {
checkout,
commit,
commitMessage,
log,
pull,
pullRebase,
push,
status,
} from "./helper/git";
import {
checkStatus,
debounce,
hidePopup,
setPluginStyle,
showPopup,
checkIsSynced,
checkStatusWithDebounce,
getPluginStyle,
} from "./helper/util";
import "./index.css";
// TODO: patch logseq Git command for the temporary fix solution
// https://github.com/haydenull/logseq-plugin-git/issues/48
try {
// @ts-ignore
top.logseq.sdk.git.exec_command(['status'])
} catch (e) {
// @ts-ignore
logseq.Git['execCommand'] = async function (args: string[]) {
const ret = await logseq.App.execGitCommand(args)
return {exitCode: ret == undefined ? 1 : 0, stdout: ret}
}
}
const isDevelopment = import.meta.env.DEV
if (isDevelopment) {
renderApp("browser");
} else {
console.log("=== logseq-plugin-git loaded ===");
logseq.ready(() => {
const operations = {
check: debounce(async function () {
const status = await checkStatus();
if (status?.stdout === "") {
logseq.UI.showMsg("No changes detected.");
} else {
logseq.UI.showMsg("Changes detected:\n" + status.stdout, "success", {
timeout: 0,
});
}
hidePopup();
}),
pull: debounce(async function () {
console.log("[faiz:] === pull click");
setPluginStyle(LOADING_STYLE);
hidePopup();
await pull(false);
checkStatus();
}),
pullRebase: debounce(async function () {
console.log("[faiz:] === pullRebase click");
setPluginStyle(LOADING_STYLE);
hidePopup();
await pullRebase();
checkStatus();
}),
checkout: debounce(async function () {
console.log("[faiz:] === checkout click");
hidePopup();
checkout();
}),
commit: debounce(async function () {
hidePopup();
await commit(true, commitMessage());
checkStatus();
}),
push: debounce(async function () {
setPluginStyle(LOADING_STYLE);
hidePopup();
await push();
checkStatus();
}),
commitAndPush: debounce(async function () {
setPluginStyle(LOADING_STYLE);
hidePopup();
const status = await checkStatus();
const changed = status?.stdout !== "";
if (changed) {
const res = await commit(
true,
commitMessage()
);
if (res.exitCode === 0) await push(true);
}
checkStatus();
}),
log: debounce(async function () {
console.log("[faiz:] === log click");
const res = await log(false);
logseq.UI.showMsg(res?.stdout, "success", { timeout: 0 });
hidePopup();
}),
showPopup: debounce(async function () {
console.log("[faiz:] === showPopup click");
showPopup();
}),
hidePopup: debounce(function () {
console.log("[faiz:] === hidePopup click");
hidePopup();
}),
};
logseq.provideModel(operations);
logseq.App.registerUIItem("toolbar", {
key: "git",
template:
'<a data-on-click="showPopup" class="button"><i class="ti ti-brand-git"></i></a><div id="plugin-git-content-wrapper"></div>',
});
logseq.useSettingsSchema(SETTINGS_SCHEMA);
setTimeout(() => {
const buttons = (logseq.settings?.buttons as string[])
?.map((title) => BUTTONS.find((b) => b.title === title))
.filter(Boolean);
if (top && buttons?.length) {
const parser = new DOMParser();
const doc = parser.parseFromString(
`
<div class="plugin-git-container">
<div class="plugin-git-mask"></div>
<div class="plugin-git-popup flex flex-col">
${buttons
.map(
(button) =>
`<button class="ui__button plugin-git-${button?.key} bg-indigo-600 hover:bg-indigo-700 focus:border-indigo-700 active:bg-indigo-700 text-center text-sm p-1" style="margin: 4px 0; color: #fff;">${button?.title}</button>`
)
.join("\n")}
</div>
`,
"text/html"
);
// remove .plugin-git-container if exists
const container = top?.document?.querySelector(".plugin-git-container");
console.log("[faiz:] === container", container);
if (container) top?.document?.body.removeChild(container);
top?.document?.body.appendChild(
doc.body.childNodes?.[0]?.cloneNode(true)
);
top?.document
?.querySelector(".plugin-git-mask")
?.addEventListener("click", hidePopup);
buttons.forEach((button) => {
top?.document
?.querySelector(`.plugin-git-${button?.key}`)
?.addEventListener("click", operations?.[button!?.event]);
});
}
}, 1000);
logseq.App.onRouteChanged(async () => {
checkStatusWithDebounce();
});
if (logseq.settings?.checkWhenDBChanged) {
logseq.DB.onChanged(({ blocks, txData, txMeta }) => {
checkStatusWithDebounce();
});
}
if (logseq.settings?.autoCheckSynced) checkIsSynced();
checkStatusWithDebounce();
if (top) {
top.document?.addEventListener("visibilitychange", async () => {
const visibilityState = top?.document?.visibilityState;
if (visibilityState === "visible") {
if (logseq.settings?.autoCheckSynced) checkIsSynced();
} else if (visibilityState === "hidden") {
// logseq.UI.showMsg(`Page is hidden: ${new Date()}`, 'success', { timeout: 0 })
// noChange void
// changed commit push
if (logseq.settings?.autoPush) {
operations.commitAndPush();
}
}
});
}
logseq.App.registerCommandPalette(
{
key: "logseq-plugin-git:commit",
label: "Commit",
keybinding: {
binding: "alt+shift+s",
mode: "global",
},
},
() => operations.commit()
);
logseq.App.registerCommandPalette(
{
key: "logseq-plugin-git:commit&push",
label: "Commit & Push",
keybinding: {
binding: "mod+s",
mode: "global",
},
},
() => operations.commitAndPush()
);
logseq.App.registerCommandPalette(
{
key: "logseq-plugin-git:rebase",
label: "Pull Rebase",
keybinding: {
binding: "mod+alt+s",
mode: "global",
},
},
() => operations.pullRebase()
);
});
}
function renderApp(env: string) {
ReactDOM.render(
<React.StrictMode>
<App env={env} />
</React.StrictMode>,
document.getElementById("root")
);
}
================================================
FILE: src/vite-env.d.ts
================================================
/// <reference types="vite/client" />
================================================
FILE: tailwind.config.js
================================================
module.exports = {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx,css}'],
theme: {
extend: {},
},
plugins: [],
}
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": false,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"noImplicitAny": false,
"jsx": "react-jsx"
},
"include": ["./src"]
}
================================================
FILE: vite.config.ts
================================================
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import usePluginImport from 'vite-plugin-importer'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
usePluginImport({
libraryName: "antd",
libraryDirectory: "es",
style: "css",
}),
],
base: './',
})
gitextract_ftc14837/ ├── .github/ │ └── workflows/ │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.html ├── package.json ├── postcss.config.js ├── release.config.js ├── src/ │ ├── App.css │ ├── App.tsx │ ├── helper/ │ │ ├── constants.ts │ │ ├── git.ts │ │ └── util.ts │ ├── index.css │ ├── main.tsx │ └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json └── vite.config.ts
SYMBOL INDEX (9 symbols across 2 files)
FILE: src/helper/constants.ts
constant COMMON_STYLE (line 3) | const COMMON_STYLE = `
constant SHOW_POPUP_STYLE (line 44) | const SHOW_POPUP_STYLE = `
constant HIDE_POPUP_STYLE (line 49) | const HIDE_POPUP_STYLE = `
constant INACTIVE_STYLE (line 55) | const INACTIVE_STYLE = `
constant ACTIVE_STYLE (line 61) | const ACTIVE_STYLE = `
constant LOADING_STYLE (line 76) | const LOADING_STYLE = `
constant BUTTONS (line 103) | const BUTTONS = [
constant SETTINGS_SCHEMA (line 114) | const SETTINGS_SCHEMA: SettingSchemaDesc[] = [
FILE: src/main.tsx
function renderApp (line 233) | function renderApp(env: string) {
Condensed preview — 20 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (35K chars).
[
{
"path": ".github/workflows/release.yml",
"chars": 682,
"preview": "name: Release\non:\n push:\n branches:\n - main\njobs:\n release:\n name: Release\n runs-on: ubuntu-20.04\n st"
},
{
"path": ".gitignore",
"chars": 45,
"preview": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
},
{
"path": "CHANGELOG.md",
"chars": 5665,
"preview": "# [1.7.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.6.0...v1.7.0) (2024-05-21)\n\n\n### Features\n\n* Added C"
},
{
"path": "LICENSE",
"chars": 1068,
"preview": "MIT License\n\nCopyright (c) 2022 Hayden Chen\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
},
{
"path": "README.md",
"chars": 2158,
"preview": "# logseq-plugin-git\n> git plugin for logseq\n\n[.name\n\nmodule.exports = {\n branches: 'main',\n plugins: [\n '@semantic-re"
},
{
"path": "src/App.css",
"chars": 0,
"preview": ""
},
{
"path": "src/App.tsx",
"chars": 586,
"preview": "import './App.css'\n\nconst App: React.FC<{ env: string }> = ({ env }) => {\n return (\n <div className=\"w-screen h-scre"
},
{
"path": "src/helper/constants.ts",
"chars": 3967,
"preview": "import { SettingSchemaDesc } from \"@logseq/libs/dist/LSPlugin.user\";\n\nexport const COMMON_STYLE = `\n.ui-items-container["
},
{
"path": "src/helper/git.ts",
"chars": 5204,
"preview": "// https://logseq.github.io/plugins/interfaces/IAppProxy.html#execGitCommand\nimport type { IGitResult } from \"@logseq/li"
},
{
"path": "src/helper/util.ts",
"chars": 2637,
"preview": "import {\n ACTIVE_STYLE,\n HIDE_POPUP_STYLE,\n INACTIVE_STYLE,\n SHOW_POPUP_STYLE,\n} from \"./constants\";\nimport { status"
},
{
"path": "src/index.css",
"chars": 146,
"preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nhtml, body {\n background-color: transparent;\n}\n#root {\n @a"
},
{
"path": "src/main.tsx",
"chars": 7013,
"preview": "import \"@logseq/libs\";\nimport React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport App from \"./App\";\nimport { BU"
},
{
"path": "src/vite-env.d.ts",
"chars": 37,
"preview": "/// <reference types=\"vite/client\" />"
},
{
"path": "tailwind.config.js",
"chars": 131,
"preview": "module.exports = {\n content: ['./index.html', './src/**/*.{js,ts,jsx,tsx,css}'],\n theme: {\n extend: {},\n },\n plug"
},
{
"path": "tsconfig.json",
"chars": 536,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"ESNext\",\n \"useDefineForClassFields\": true,\n \"lib\": [\"DOM\", \"DOM.Iterable\","
},
{
"path": "vite.config.ts",
"chars": 344,
"preview": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport usePluginImport from 'vite-plugin-im"
}
]
About this extraction
This page contains the full source code of the haydenull/logseq-plugin-git GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 20 files (31.6 KB), approximately 9.7k tokens, and a symbol index with 9 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.