Embed the viewer anywhere; plug into GitHub, Vim, Neovim, Emacs and J… · initialcommit-com/git-sim@27bf3a5 · GitHub
Skip to content

Commit 27bf3a5

Browse files
Embed the viewer anywhere; plug into GitHub, Vim, Neovim, Emacs and Jupyter
git-sim-embed.js turns a git-sim graph into the interactive viewer on any web page: <div class="git-sim" data-src="graph.svg"> plus one script tag. Each embed is an iframe carrying the viewer's stylesheet and script, so several on a page never share ids or key bindings and the host's styles stay out; the host fetches the SVG and posts it in, the frame reports its height back. Attributes pin a state, pick a theme, drop the brand links or fix a height. --img-format svg writes the graph it embeds. The script is exported with the viewer assets (render/embed.py). GitHub: a gh extension (gh sim <args>; gh sim pr N [rebase] simulates a pull request's merge or rebase in a temporary worktree, never touching the checkout) and a composite Action that comments on each pull request with the pre-flight report, new `git-sim preflight --markdown`, and attaches the interactive graph as an artifact, one comment per PR updated on later pushes. Editors: a Neovim plugin (Lua), a Vim 8.1 plugin (Vimscript) and an Emacs package, each with simulate, pre-flight in a window and live start/stop, taking the command from the region or the current line; the Emacs one offers Magit's branch or commit at point. The Vim plugin loads in Git for Windows' Vim; the Emacs package byte-compiles and runs in batch on Emacs 29. Jupyter: %load_ext git_sim.jupyter, then %gitsim <command> shows the page inline in a sandboxed iframe, %gitsim preflight prints the report. docs/integrations.md maps every place git-sim plugs in, including the shell route for nano; the folders under integrations/ say which need a repository of their own to be installable the usual way. Signed-off-by: Jacob Stopak <jacob@initialcommit.io>
1 parent eb48407 commit 27bf3a5

25 files changed

Lines changed: 1355 additions & 6 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 1 deletion

‎docs/embed.md‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Embedding a git-sim graph in a page
2+
3+
Any blog post, tutorial or documentation page can show a git-sim graph with
4+
the full interactive viewer: the Before / After slider and play button, hover
5+
details, zoom, sharing. Two lines:
6+
7+
```html
8+
<div class="git-sim" data-src="/img/rebase-main.svg" data-title="git rebase main"></div>
9+
<script src="https://initialcommit.com/js/tools/git-sim-embed.js" defer></script>
10+
```
11+
12+
The script replaces every `.git-sim` element with the viewer showing its graph.
13+
Include it once; it finds all of them.
14+
15+
## Making the graph
16+
17+
```
18+
git-sim --img-format svg rebase main
19+
```
20+
21+
writes the graph alone (the interactive page's SVG, with the before / after
22+
data the viewer plays) under `git-sim media-dir`. Copy it next to your page.
23+
"Download SVG" in any git-sim page's Share menu gives the same file. A saved
24+
page (`.html`) works too and is framed as it is.
25+
26+
## Attributes
27+
28+
| Attribute | Meaning |
29+
| --- | --- |
30+
| `data-src` | The SVG (or saved page) to show. Same origin as your page, or a host that allows cross-origin reads. |
31+
| `data-title` | The command, for the share text and the frame's title. |
32+
| `data-state` | `before`, `after` or `step=N` pins the graph there; without it the graph plays on a loop. |
33+
| `data-theme` | `dark` or `light`. Default: your reader's colour-scheme preference. |
34+
| `data-controls` | `compact` drops the brand and partner links, keeping the slider and Share. |
35+
| `data-height` | A fixed height (`480px`). Default: the embed takes the height the graph needs. |
36+
37+
## How it works
38+
39+
Each embed is an iframe with a self-contained document (the script carries the
40+
viewer's stylesheet and script), so several graphs on one page never share ids
41+
or keyboard shortcuts, and your page's styles never reach the graph. Your page
42+
fetches the SVG itself and hands the text to the frame, so there is nothing to
43+
configure on a server; the frame reports its height back so the embed fits the
44+
graph exactly. `GitSimEmbed.scan(root)` mounts elements added later, for pages
45+
that render content dynamically.
46+
47+
The script is exported from the git-sim package (`python -m git_sim.render.html`)
48+
alongside the viewer assets, so it is the same viewer as the pages git-sim
49+
writes and the one at initialcommit.com.

‎docs/integrations.md‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Where git-sim plugs in
2+
3+
git-sim answers three questions (what would this do, is this safe, what just
4+
happened) wherever Git is used. The engine is the command line; everything
5+
below is a thin layer over it.
6+
7+
| Place | What | Where it lives |
8+
| --- | --- | --- |
9+
| Terminal | `git-sim`, `git sim`, `git preflight`, `git live`; lazygit and tig bindings | [shell.md](shell.md) |
10+
| Browser | the viewer and the live page at initialcommit.com; the graph never leaves your machine | [live.md](live.md) |
11+
| VS Code, Cursor, Windsurf, VSCodium | the extension: simulate, pre-flight, live tab and sidebar view, walkthrough | [vscode.md](vscode.md) |
12+
| Neovim | `:GitSim`, `:GitSimPreflight`, `:GitSimLive` | [integrations/nvim](../integrations/nvim) |
13+
| Vim 8.1+ | the same commands in Vimscript | [integrations/vim](../integrations/vim) |
14+
| Emacs 27.1+ | `git-sim-simulate`, `git-sim-preflight`, `git-sim-live`; Magit's branch or commit at point as the default | [integrations/emacs](../integrations/emacs) |
15+
| nano, and any editor without plugins | the shell: `git sim`, `git preflight`, and `git live` in a second pane; nano's execute prompt (`^T`) can insert a pre-flight report into the buffer | [shell.md](shell.md) |
16+
| Jupyter | `%load_ext git_sim.jupyter`, then `%gitsim rebase main` shows the graph inline | below |
17+
| GitHub CLI | `gh sim pr 42`: what merging a pull request would do, in a temporary worktree | [integrations/gh-sim](../integrations/gh-sim) |
18+
| GitHub Actions | a comment on each pull request with the pre-flight report and the graph as an artifact | [integrations/github-action](../integrations/github-action) |
19+
| Blogs and docs | `git-sim-embed.js`: the interactive viewer around any git-sim SVG | [embed.md](embed.md) |
20+
| AI agents | the pre-flight hook and MCP server, wired by `git-sim install` | [mcp.md](mcp.md) |
21+
22+
## Jupyter
23+
24+
```
25+
%load_ext git_sim.jupyter
26+
%gitsim rebase main
27+
%gitsim --height 700 -C ../other-repo merge feature
28+
%gitsim preflight reset --hard HEAD~1
29+
```
30+
31+
The command runs in the notebook's working directory (or the `-C` path) as it
32+
would in a terminal, with the browser kept closed, and the page git-sim wrote is
33+
shown in an iframe under the cell so its scripts and styles stay out of the
34+
notebook's. `preflight` prints the report as text. Nothing in the repository is
35+
changed. Needs git-sim installed in the notebook's kernel environment.
36+
37+
## Publishing the pieces that need their own repository
38+
39+
Three of these live here only as source and want a repository of their own to
40+
be installable by the usual command: `gh extension install` expects a
41+
repository named `gh-sim` with the `gh-sim` executable at its root, and Vim
42+
and Neovim plugin managers expect `plugin/` (and `lua/`) at a repository's
43+
root. Each folder's README says what to copy where.

‎docs/shell.md‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,16 @@ completion for its commands and options to bash, zsh, fish or PowerShell.
7878
## In an editor
7979
8080
The [VS Code extension](vscode.md) puts all three verbs in the editor, with a
81-
live view for the sidebar. For editors without an extension, `git-sim live`
82-
in a terminal beside the editor does most of the work: it opens in the
83-
browser and follows whatever the editor's Git integration does.
81+
live view for the sidebar; [Vim](../integrations/vim), [Neovim](../integrations/nvim)
82+
and [Emacs](../integrations/emacs) have plugins with the same commands. For
83+
editors without a plugin system, nano among them, the terminal does the work:
84+
`git-sim live` in a pane beside the editor (tmux, a second tab) opens the
85+
graph in the browser and follows whatever you do in the editor's own Git
86+
commands. In nano 5 or newer, the execute prompt (`^T`, "Execute Command")
87+
runs a shell command and inserts its output, so
88+
`git-sim preflight reset --hard HEAD~1` there drops the report into the
89+
buffer you are writing in, which is handy for notes and pull request
90+
descriptions.
8491
8592
## Beside an AI agent
8693

‎integrations/emacs/README.md‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# git-sim.el
2+
3+
git-sim in Emacs 27.1 or newer: simulate a Git command on a before / after
4+
graph of the repository, pre-flight a risky one, or follow the repository
5+
live. A thin client over the `git-sim` command line, which must be installed
6+
(`pipx install git-sim`).
7+
8+
```
9+
M-x git-sim-simulate RET rebase main the graph opens in the browser
10+
M-x git-sim-preflight RET reset --hard HEAD~1 the report, in a buffer (q closes)
11+
M-x git-sim-live follow this repository live
12+
M-x git-sim-live-stop
13+
```
14+
15+
With a region active, or on a line holding a git command, the command is
16+
offered as the default (a leading `$`, `git` or `git-sim` is dropped). In a
17+
Magit buffer, the branch or commit at point is offered: `merge <branch>`,
18+
`reset <commit>`.
19+
20+
## Install
21+
22+
Copy `git-sim.el` somewhere on your `load-path`, or with `use-package` once
23+
this folder is its own repository (`initialcommit-com/git-sim.el`):
24+
25+
```elisp
26+
(use-package git-sim
27+
:vc (:url "https://github.com/initialcommit-com/git-sim.el") ; Emacs 30
28+
:custom
29+
(git-sim-arguments '("--all"))
30+
:bind (("C-c g s" . git-sim-simulate)
31+
("C-c g p" . git-sim-preflight)
32+
("C-c g l" . git-sim-live)))
33+
34+
(with-eval-after-load 'magit
35+
(define-key magit-mode-map (kbd "C-c s") #'git-sim-simulate)
36+
(define-key magit-mode-map (kbd "C-c p") #'git-sim-preflight))
37+
```
38+
39+
Customize `git-sim-executable`, `git-sim-arguments` and `git-sim-live-arguments`.

‎integrations/emacs/git-sim.el‎

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
;;; git-sim.el --- Simulate, pre-flight and follow Git with git-sim -*- lexical-binding: t; -*-
2+
3+
;; Author: Initial Commit <jacob@initialcommit.io>
4+
;; URL: https://github.com/initialcommit-com/git-sim
5+
;; Version: 0.1.0
6+
;; Package-Requires: ((emacs "27.1"))
7+
;; Keywords: vc, git, tools
8+
9+
;;; Commentary:
10+
11+
;; git-sim in Emacs: simulate a Git command on a before / after graph of the
12+
;; repository, pre-flight a risky one for the facts, or follow the repository
13+
;; live, from the editor. A thin client over the `git-sim' command line, which
14+
;; must be installed (pipx install git-sim).
15+
;;
16+
;; M-x git-sim-simulate RET rebase main the graph opens in the browser
17+
;; M-x git-sim-preflight RET reset --hard HEAD~1
18+
;; the report, in a buffer
19+
;; M-x git-sim-live follow this repository live
20+
;; M-x git-sim-live-stop
21+
;;
22+
;; With a region active, or on a line that holds a git command, the command is
23+
;; taken from there (a leading "$", "git" or "git-sim" is dropped).
24+
;;
25+
;; Magit users: bind the commands in `magit-mode-map', for example
26+
;; (with-eval-after-load 'magit
27+
;; (define-key magit-mode-map (kbd "C-c s") #'git-sim-simulate)
28+
;; (define-key magit-mode-map (kbd "C-c p") #'git-sim-preflight)
29+
;; (define-key magit-mode-map (kbd "C-c l") #'git-sim-live))
30+
;; and `git-sim-simulate' on a Magit section offers the commit or branch at
31+
;; point as the default argument.
32+
33+
;;; Code:
34+
35+
(defgroup git-sim nil
36+
"See what Git is doing to your repository with git-sim."
37+
:group 'vc
38+
:prefix "git-sim-")
39+
40+
(defcustom git-sim-executable "git-sim"
41+
"The git-sim program."
42+
:type 'string)
43+
44+
(defcustom git-sim-arguments nil
45+
"Extra global options for every run, for example (\"--all\")."
46+
:type '(repeat string))
47+
48+
(defcustom git-sim-live-arguments nil
49+
"Extra options for `git-sim live', for example (\"--no-zones\")."
50+
:type '(repeat string))
51+
52+
(defvar git-sim--live-process nil "The running `git-sim live' process, if any.")
53+
54+
(defun git-sim--repository ()
55+
"The directory git-sim runs in: the current file's, else `default-directory'."
56+
(or (and buffer-file-name (file-name-directory buffer-file-name))
57+
default-directory))
58+
59+
(defun git-sim--clean (text)
60+
"TEXT without a leading prompt, git or git-sim."
61+
(let ((s (string-trim text)))
62+
(setq s (replace-regexp-in-string "\\`[$#>]+[ \t]*" "" s))
63+
(setq s (replace-regexp-in-string "\\`git-sim[ \t]+" "" s))
64+
(setq s (replace-regexp-in-string "\\`git[ \t]+" "" s))
65+
(string-trim s)))
66+
67+
(defun git-sim--default-command ()
68+
"A git command found at point: the region, a Magit object, or the line."
69+
(cond
70+
((use-region-p)
71+
(git-sim--clean (buffer-substring-no-properties (region-beginning) (region-end))))
72+
((and (fboundp 'magit-branch-at-point) (magit-branch-at-point))
73+
(concat "merge " (magit-branch-at-point)))
74+
((and (fboundp 'magit-commit-at-point) (magit-commit-at-point))
75+
(concat "reset " (magit-commit-at-point)))
76+
((let ((line (thing-at-point 'line t)))
77+
(and line (string-match-p "\\(git\\|git-sim\\)[ \t]" line)
78+
(git-sim--clean line))))
79+
(t "")))
80+
81+
(defun git-sim--read-command (prompt)
82+
"Ask for a git command, offering what is at point."
83+
(let ((default (git-sim--default-command)))
84+
(git-sim--clean
85+
(read-string (if (string-empty-p default) prompt (format "%s (%s): " (string-trim-right prompt ": ") default))
86+
nil 'git-sim-history default))))
87+
88+
(defun git-sim--words (command)
89+
(split-string command "[ \t]+" t))
90+
91+
;;;###autoload
92+
(defun git-sim-simulate (command)
93+
"Simulate the git COMMAND on this repository; the graph opens in the browser."
94+
(interactive (list (git-sim--read-command "git-sim: ")))
95+
(when (string-empty-p command)
96+
(user-error "git-sim: give a command, for example rebase main"))
97+
(let* ((default-directory (git-sim--repository))
98+
(buffer (generate-new-buffer " *git-sim*"))
99+
(process (apply #'start-process "git-sim" buffer git-sim-executable
100+
(append git-sim-arguments (git-sim--words command)))))
101+
(set-process-sentinel
102+
process
103+
(lambda (proc _event)
104+
(when (memq (process-status proc) '(exit signal))
105+
(let ((code (process-exit-status proc))
106+
(output (with-current-buffer (process-buffer proc)
107+
(string-trim (buffer-string)))))
108+
(kill-buffer (process-buffer proc))
109+
(if (zerop code)
110+
(message "git-sim: simulated git %s" command)
111+
(message "git-sim: git %s failed: %s" command output))))))
112+
(message "git-sim: simulating git %s..." command)))
113+
114+
;;;###autoload
115+
(defun git-sim-preflight (command)
116+
"Show what the git COMMAND would do: risk, facts, losses, how to undo it."
117+
(interactive (list (git-sim--read-command "git-sim preflight: ")))
118+
(when (string-empty-p command)
119+
(user-error "git-sim: give a command, for example reset --hard HEAD~1"))
120+
(let* ((default-directory (git-sim--repository))
121+
(buffer (get-buffer-create (format "*git-sim preflight: git %s*" command))))
122+
(with-current-buffer buffer
123+
(let ((inhibit-read-only t))
124+
(erase-buffer)
125+
(apply #'call-process git-sim-executable nil buffer nil
126+
"preflight" (git-sim--words command))
127+
(goto-char (point-min)))
128+
(special-mode))
129+
(pop-to-buffer buffer)))
130+
131+
;;;###autoload
132+
(defun git-sim-live ()
133+
"Follow this repository live: git-sim serves the page and opens the browser."
134+
(interactive)
135+
(if (and git-sim--live-process (process-live-p git-sim--live-process))
136+
(message "git-sim: live mode is already running (git-sim-live-stop ends it)")
137+
(let ((default-directory (git-sim--repository)))
138+
(setq git-sim--live-process
139+
(apply #'start-process "git-sim live" " *git-sim live*" git-sim-executable
140+
(append git-sim-arguments (list "live") git-sim-live-arguments)))
141+
(set-process-filter
142+
git-sim--live-process
143+
(lambda (proc text)
144+
(when (string-match "^[ \t]+\\(https?://[^ \n]+\\)" text)
145+
(message "git-sim: live at %s" (match-string 1 text)))
146+
(with-current-buffer (process-buffer proc) (goto-char (point-max)) (insert text))))
147+
(set-process-sentinel
148+
git-sim--live-process
149+
(lambda (proc _event)
150+
(when (memq (process-status proc) '(exit signal))
151+
(setq git-sim--live-process nil)
152+
(message "git-sim: live mode ended"))))
153+
(message "git-sim: live mode started; the page opens in your browser"))))
154+
155+
;;;###autoload
156+
(defun git-sim-live-stop ()
157+
"Stop live mode."
158+
(interactive)
159+
(if (and git-sim--live-process (process-live-p git-sim--live-process))
160+
(progn (interrupt-process git-sim--live-process)
161+
(run-at-time 2 nil (lambda () (when (and git-sim--live-process (process-live-p git-sim--live-process))
162+
(kill-process git-sim--live-process)))))
163+
(message "git-sim: live mode is not running")))
164+
165+
(provide 'git-sim)
166+
;;; git-sim.el ends here

‎integrations/gh-sim/README.md‎

Lines changed: 25 additions & 0 deletions

0 commit comments

Comments
 (0)