Skip to content

Commit 3828b2d

Browse files
authored
feat(plugins): add @devframes/plugin-git Git dashboard (#38)
1 parent 49bc3bd commit 3828b2d

75 files changed

Lines changed: 5994 additions & 461 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

examples/next-runtime-snapshot/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"next-runtime-snapshot": "./bin.mjs"
1111
},
1212
"scripts": {
13-
"build": "next build src/client && rm -rf dist/client && mkdir -p dist && cp -r src/client/out dist/client",
13+
"build": "next build src/client && node scripts/build-spa.mjs",
1414
"cli:build": "node bin.mjs build --out-dir dist/static",
1515
"dev": "node bin.mjs",
1616
"next:dev": "next dev src/client",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { cpSync, mkdirSync, rmSync } from 'node:fs'
2+
3+
rmSync('dist/client', { recursive: true, force: true })
4+
mkdirSync('dist', { recursive: true })
5+
cpSync('src/client/out', 'dist/client', { recursive: true })

plugins/code-server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"scripts": {
4343
"build": "tsdown && vite build --config src/spa/vite.config.ts",
4444
"watch": "tsdown --watch",
45+
"typecheck": "tsc --noEmit",
4546
"dev": "vite --config src/spa/vite.config.ts --host 0.0.0.0",
4647
"storybook": "storybook dev -p 6006 --host 0.0.0.0",
4748
"build-storybook": "storybook build",

plugins/code-server/tsconfig.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"extends": "../../tsconfig.base.json",
33
"compilerOptions": {
4-
"composite": true,
54
"lib": ["esnext", "dom"]
6-
}
5+
},
6+
"include": ["src", "test", "tsdown.config.ts"],
7+
"exclude": ["dist", "src/spa/dist", ".next", "out", "node_modules"]
78
}

plugins/git/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.next
2+
dist
3+
next-env.d.ts
4+
node_modules
5+
out
6+
.turbo
7+
storybook-static

plugins/git/.storybook/main.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { StorybookConfig } from '@storybook/react-vite'
2+
import tailwindcss from '@tailwindcss/vite'
3+
import react from '@vitejs/plugin-react-oxc'
4+
5+
const config: StorybookConfig = {
6+
stories: ['../src/client/**/*.stories.@(ts|tsx)'],
7+
addons: ['@storybook/addon-docs', '@storybook/addon-a11y'],
8+
framework: {
9+
name: '@storybook/react-vite',
10+
options: {},
11+
},
12+
viteFinal(viteConfig) {
13+
viteConfig.plugins ??= []
14+
// Vite 8 bundles with rolldown/oxc, which doesn't transform JSX on its
15+
// own; the React plugin wires up the automatic runtime so `.tsx` stories
16+
// and views parse.
17+
viteConfig.plugins.push(react(), tailwindcss())
18+
return viteConfig
19+
},
20+
}
21+
22+
export default config

plugins/git/.storybook/preview.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { Decorator, Preview } from '@storybook/react-vite'
2+
import { useEffect } from 'react'
3+
import '../src/client/app/globals.css'
4+
5+
const withTheme: Decorator = (Story, context) => {
6+
const theme = context.globals.theme ?? 'dark'
7+
useEffect(() => {
8+
document.documentElement.classList.toggle('dark', theme === 'dark')
9+
}, [theme])
10+
return (
11+
<div className="bg-background text-foreground flex h-svh justify-center p-6">
12+
<div className="flex h-full w-full max-w-2xl flex-col overflow-hidden rounded-lg border p-3">
13+
<Story />
14+
</div>
15+
</div>
16+
)
17+
}
18+
19+
const preview: Preview = {
20+
parameters: {
21+
layout: 'fullscreen',
22+
controls: { expanded: true },
23+
},
24+
globalTypes: {
25+
theme: {
26+
description: 'Color theme',
27+
defaultValue: 'dark',
28+
toolbar: {
29+
title: 'Theme',
30+
icon: 'contrast',
31+
items: [
32+
{ value: 'light', title: 'Light', icon: 'sun' },
33+
{ value: 'dark', title: 'Dark', icon: 'moon' },
34+
],
35+
dynamicTitle: true,
36+
},
37+
},
38+
},
39+
decorators: [withTheme],
40+
}
41+
42+
export default preview

plugins/git/.storybook/shims.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Storybook config files live outside the app's tsconfig, so declare the CSS
2+
// side-effect import used by `preview.tsx`.
3+
declare module '*.css'

plugins/git/LICENSE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026-PRESENT Anthony Fu <https://github.com/antfu>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

plugins/git/README.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# @devframes/plugin-git
2+
3+
Git integration for [devframe](https://github.com/devframes/devframe) — a
4+
repository dashboard with a **Next.js App Router + shadcn/ui** SPA over
5+
type-safe RPC. The host process shells out to `git` and exposes the repository;
6+
the same bundle runs as a live dev server or a fully static deployment.
7+
8+
Status, a SourceTree-style **commit graph**, branches, and diffs are read-only;
9+
staging, unstaging, and committing are available when write mode is enabled. The
10+
UI follows the system **light/dark** preference with a manual toggle.
11+
12+
## Install
13+
14+
```sh
15+
npm i -D @devframes/plugin-git
16+
```
17+
18+
## Standalone CLI
19+
20+
Run the dashboard against the current repository:
21+
22+
```sh
23+
npx devframe-git # dev server (live RPC over WebSocket)
24+
npx devframe-git --write # also enable staging / committing from the UI
25+
npx devframe-git build # static deploy → dist-static/
26+
npx devframe-git --port 4000
27+
```
28+
29+
## Programmatic
30+
31+
`createGitDevframe(options)` returns a devframe definition you can mount into
32+
any host with devframe's adapters, or drive yourself.
33+
34+
```ts
35+
import { createGitDevframe } from '@devframes/plugin-git'
36+
import { createCli } from 'devframe/adapters/cli'
37+
38+
await createCli(createGitDevframe({ repoRoot: process.cwd() })).parse()
39+
```
40+
41+
| Option | Default | Description |
42+
|--------|---------|-------------|
43+
| `repoRoot` | the devframe `cwd` | Repository directory to inspect. |
44+
| `basePath` | adapter-resolved | Mount path (`/` standalone, `/__git/` hosted). |
45+
| `distDir` | bundled SPA | Override the served SPA directory. |
46+
| `port` | `9710` | Preferred dev-server port. |
47+
| `write` | `false` | Enable staging, unstaging, and committing from the UI. |
48+
49+
## RPC surface
50+
51+
The read functions are each a `query` with `snapshot: true`: resolved live over
52+
WebSocket in dev, and served from a snapshot baked at build time for static
53+
deploys. Each degrades to an empty, `isRepo: false` result outside a git
54+
repository.
55+
56+
- `git:status` — branch, upstream tracking (ahead/behind), staged / unstaged /
57+
untracked files, parsed from `git status --porcelain=v2`. Reports `canWrite`.
58+
- `git:log` — paginated commit history (`limit` / `skip`) including parent
59+
hashes, which drive the commit graph.
60+
- `git:branches` — local branches with SHA, upstream, ahead/behind, tip subject.
61+
- `git:diff` — per-file added/deleted counts for the working tree or index, plus
62+
a unified patch for a selected file.
63+
64+
Write actions are `action` functions, registered only when write mode is enabled
65+
(`createGitDevframe({ write: true })` or the `--write` flag) and gated behind
66+
`status.canWrite` in the UI. Each returns fresh status (commit returns a result):
67+
68+
- `git:stage``git add` the given paths.
69+
- `git:unstage``git restore --staged` the given paths.
70+
- `git:commit` — commit the staged changes with a message.
71+
72+
## Develop
73+
74+
```sh
75+
pnpm -C plugins/git dev # client (Next.js HMR) + RPC backend together
76+
pnpm -C plugins/git build # tsdown (node) + next build (SPA) → dist/
77+
```
78+
79+
`pnpm dev` starts the Next.js dev server (with hot-reload) and the devframe
80+
RPC/WebSocket backend at the same time, then prints both URLs — open the UI one.
81+
The SPA connects to the backend over the WebSocket port carried in
82+
`NEXT_PUBLIC_DEVFRAME_WS`. Override ports with `PORT` (UI) and
83+
`DEVFRAME_GIT_PORT` (backend). Run a single side with `dev:client` or
84+
`dev:server`.
85+
86+
The SPA is a standard shadcn/ui setup (Tailwind v4, `components/ui/*`). Three
87+
Next.js settings in `src/client/next.config.mjs` keep it portable: `output:
88+
'export'` (devframe owns the server), `assetPrefix: '.'` (relative assets so the
89+
same bundle works at any base), and `trailingSlash: true` (composes with
90+
devframe's static directory-with-index resolution).

0 commit comments

Comments
 (0)