Inspiration
As programmers, we live and breathe Git. It's our safety net, our collaboration tool, and the foundation of modern software development. When I started using powerful, collaborative design tools like Adobe Express, I was amazed that they only offered a basic, linear version history. I saw a huge opportunity.
My Inspiration was to bridge this gap: to bring the power, safety, and collaborative potential of Git to the creative space. I wanted to build a tool that would let designers experiment fearlessly on branches, compare versions side-by-side, and merge features with confidence. With GitExpress, the chaos of "logo_final_v3_USE_THIS_ONE.aepx" is over.
What it does
GitExpress is a fully-featured version control system that runs directly inside the Adobe Express add-on panel. It gives designers the professional-grade workflow that developers have relied on for decades:
- Commit History: Save a complete snapshot of your document at any time with a descriptive message. The history is displayed in a clean, visual timeline with auto-generated thumbnails.
- Branching: Create a new branch to try a radical new idea (like a new color scheme or layout) without any fear of messing up your main design.
- Switching: Instantly switch between branches to compare different creative directions. The document on your canvas updates immediately to reflect the selected version.
- Merging: Seamlessly combine your work. GitExpress implements a true three-way merge that can handle diverged branches, creating a merge commit that ties the histories back together.
- Visual Comparison & Reverting: Select any two commits in your history to see a side-by-side visual comparison of the changes. If you make a mistake, you can revert any commit with a single click, creating a new commit that undoes the changes safely.
- Visual Git Graph: Toggle from the list view to a dynamic graph, powered by Mermaid.js, to see a beautiful visualization of your entire project structure, including all branches and merges.
How I built it
GitExpress is a 100% client-side application built with React and TypeScript. All version history is stored locally and efficiently in the user's browser using IndexedDB, managed by the powerful Dexie.js library.
- UI: The interface is built with Adobe's Spectrum Web Components to feel native and familiar to Express users.
- Delta Engine: To ensure high performance and minimal storage usage, commits are not full snapshots. I used
fast-json-patchto calculate the difference between document states and a WebAssembly-poweredlz4-wasmmodule to compress the resulting patches, achieving over 95% storage reduction on typical changes. - Serialization: The add-on features a sophisticated, recursive serialization engine built from scratch to capture the state of complex, grouped objects on the canvas. (Not every object type is able to be captured with this demo; capture is limited to text, rectangle, ellipse, groups, and images as in the code snippet below:)
switch (childData.type) {
case "Text":
case "StandaloneText":
newNode = editor.createText(childData.text || " ");
break;
case "Rectangle":
newNode = editor.createRectangle();
break;
case "Ellipse":
newNode = editor.createEllipse();
break;
case "Group":
newNode = editor.createGroup();
break;
case "ImageNode":
if (childData.fill && childData.fill.type === "Image") {
newNode = editor.createImageContainer(childData.fill);
}
break;
default:
continue;
}
refer to the attached codebase for more info on the implementation.
- Git Logic: All branching, merging (including fast-forwards and three-way merges), and reverting logic was implemented from the ground up to mimic the true, robust behavior of Git.
Challenges I ran into
My greatest challenge was the secure, sandboxed nature of the Add-on environment. I spent the majority of my development time on a heroic debugging marathon to solve a series of deep, interconnected issues:
- The Infinite Resizing Bug: My first major hurdle was a maddening bug where restoring a simple shape would cause it to inexplicably change size. After days of failed attempts, I built a diagnostic tool that gave me the breakthrough discovery: the document nodes are opaque proxy objects. Their internal properties cannot be programmatically listed or inspected. The bug wasn't in our logic, but a fundamental limitation of the environment. Our solution was to engineer a robust serializer that explicitly captures every known property (translation, fill, radiusX, textContent, etc.), which finally stabilized the system.
- SDK Inconsistencies: I battled numerous "ghost" errors caused by subtle differences between the documentation and the live SDK. I discovered that
app.createRenditionwas unavailable, forcing us to pivot to a client-side SVG thumbnail generator. I debugged modal dialogs that required undocumented properties and had to reverse-engineer the correct names for event results (buttonTypevs.buttonId). - Database Schema Evolution: As I added features like merge commits and thumbnails, I had to carefully manage our IndexedDB schema versions with Dexie.js, learning firsthand how critical it is to clear old, incompatible data during rapid development.
Overcoming these challenges required immense perseverance and a commitment to systematic, evidence-based debugging. The final, stable application is a testament to this difficult but rewarding journey.
Accomplishments that I'm proud of
I am incredibly proud of building a complete, end-to-end version control system from scratch in a challenging environment. Specifically:
- The Recursive Serializer: Building a function that could correctly capture and restore complex, nested groups of shapes and text without being able to inspect the objects was a huge technical achievement.
- The Three-Way Merge: Implementing a true three-way merge that can find a common ancestor and automatically combine divergent histories is the most complex feature in Git, and I successfully replicated it.
- Perseverance: The debugging process was a marathon. I am most proud of my persistence in the face of repeated, frustrating failures. I refused to give up, and the final, working product is the result of that commitment.
What I learned
- The Sandbox is Opaque: You cannot treat sandbox objects like normal JavaScript objects. You must write defensive code that only accesses explicitly documented properties.
- State Management is Key: In a complex React app with many asynchronous operations, relying on component state for critical logic can lead to subtle race conditions. The most robust solution is to always fetch the "single source of truth" directly from the database before performing an action.
- Systematic Debugging Works: When faced with impossible bugs, the only way forward is to stop guessing and start gathering data. Building small diagnostic tools and adding
console.logstatements at every step was the key to solving every major problem I faced.
What's next for GitXpress
The roadmap has brought us to a feature-complete and stable prototype. Our next steps are:
- Phase 1 (Final Polish & Collaboration): The first month will be dedicated to hardening the add-on for a public release. This includes: Refining the user interface based on initial feedback, Collaborating directly with Adobe Express team to investigate solutions for the sandbox limitations we discovered, specifically to find a way to achieve perfect, 1:1 object serialization and solve the "resizing bug", Launching a public beta with a select group of power users and creative teams to gather real-world usage data, and include a mechanism to capture most/all shape types in adobe express.
- Phase 2 (Public Launch): Submit to the official Adobe Express Add-on Marketplace to bring GitExpress to the entire creative community.
- Phase 3 (The Cloud): The ultimate vision for GitExpress is to add a premium feature that allows users to connect their local repository to a remote server (like GitHub or a private server). This would enable true cross-machine syncing and team collaboration, making GitExpress an indispensable tool for professional design teams.
Built With
- adobe-express-add-on-sdk
- dexie.js
- fast-json-patch
- indexeddb
- lz4-wasm
- mermaid.js
- react
- swc
- typescript
Log in or sign up for Devpost to join the conversation.