Skip to content
Closed
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
11 changes: 11 additions & 0 deletions website/.well-known/assetlinks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.stabilitynexus.zplit",
"sha256_cert_fingerprints":
["FA:C6:17:45:DC:09:03:78:6F:B9:ED:E6:2A:96:2B:39:9F:73:48:F0:BB:6F:89:9B:83:32:66:75:91:03:3B:9C"]
}
}
]
Comment on lines +1 to +11
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Search for keystore/signing configuration files
find . -type f \( -name "*.keystore" -o -name "*.jks" -o -name "*.p12" -o -name "*.pfx" \) 2>/dev/null | head -20

Repository: StabilityNexus/Zplit

Length of output: 46


🏁 Script executed:

# Search for references to the package name in the codebase
rg "com\.stabilitynexus\.zplit" --type-add="json:*.json" -t json -A 3 -B 3

Repository: StabilityNexus/Zplit

Length of output: 46


🏁 Script executed:

# Look for signing or keystore configuration in build files
rg -i "signing|keystore|certificate|sha256" --type-add="gradle:*.gradle" --type-add="gradle:*.gradle.kts" -t gradle -l | head -10

Repository: StabilityNexus/Zplit

Length of output: 92


🏁 Script executed:

# Check for other assetlinks.json files
find . -name "assetlinks.json" -type f

Repository: StabilityNexus/Zplit

Length of output: 101


🏁 Script executed:

# Validate the fingerprint format (should be valid hex with colons)
python3 << 'EOF'
fingerprint = "FA:C6:17:45:DC:09:03:78:6F:B9:ED:E6:2A:96:2B:39:9F:73:48:F0:BB:6F:89:9B:83:32:66:75:91:03:3B:9C"
parts = fingerprint.split(":")
print(f"Fingerprint format validation:")
print(f"  Total parts: {len(parts)}")
print(f"  Expected for SHA256: 32 parts")
print(f"  All parts are 2-char hex: {all(len(p) == 2 and all(c in '0123456789ABCDEFabcdef' for c in p) for p in parts)}")
print(f"  Format is valid: {len(parts) == 32 and all(len(p) == 2 and all(c in '0123456789ABCDEFabcdef' for c in p) for p in parts)}")
EOF

Repository: StabilityNexus/Zplit

Length of output: 201


🏁 Script executed:

cat -n android/app/build.gradle.kts

Repository: StabilityNexus/Zplit

Length of output: 1753


Update the package name in assetlinks.json to match the app's applicationId.

The assetlinks.json references com.stabilitynexus.zplit, but the app's actual applicationId in android/app/build.gradle.kts is com.example.zplit (line 24). This mismatch will prevent Android App Links from working. Update the package_name to match your configured applicationId before verifying the certificate fingerprint. Additionally, the release build currently uses debug signing keys (line 37)—add proper signing configuration for production.

🤖 Prompt for AI Agents
In website/.well-known/assetlinks.json around lines 1 to 11, the package_name is
set to com.stabilitynexus.zplit but the app's applicationId is
com.example.zplit; update package_name to com.example.zplit to match
android/app/build.gradle.kts (line 24), confirm the SHA-256 fingerprint is the
release key you will use, and ensure the Android release build uses production
signing keys instead of debug keys (fix signing config in
android/app/build.gradle.kts around line 37) before verifying App Links.

201 changes: 201 additions & 0 deletions website/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Zplit - Decentralized Expense Splitting</title>
<meta name="description" content="Zplit is a privacy-focused, offline-first, decentralized expense splitting app.">
<link rel="stylesheet" href="styles.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="manifest" href="/manifest.json">
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing manifest.json file.

The HTML references /manifest.json but this file is not included in the PR. This will result in a 404 error.

Do you want me to generate a basic web app manifest file for you, or will you be adding it separately?

🤖 Prompt for AI Agents
In website/index.html around line 13, the page links to /manifest.json but that
file is missing causing a 404; add a new manifest.json at the project root (or
adjust the link to the correct path) containing basic required fields (name,
short_name, start_url, scope, display, icons array with at least one icon entry
and sizes/type, background_color, theme_color) so browsers can load the PWA
manifest; commit the manifest.json and ensure the href in index.html points to
its correct location.

</head>

<body>
<nav class="navbar">
<div class="container">
<a href="#" class="logo">Zplit<span class="dot">.</span></a>
<div class="nav-links">
<a href="#features">Features</a>
<a href="#workflow">How it Works</a>
<a href="#download" class="btn btn-primary">Download App</a>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

"Download App" and "Get Early Access" links point to non-existent anchor.

Both CTA buttons link to #download, but there's no corresponding download section on the page. This creates a broken user experience.

Consider either:

  1. Adding a download section with app store links, or
  2. Updating the links to point to a relevant destination (e.g., GitHub releases, waitlist form)

Also applies to: 40-41

🤖 Prompt for AI Agents
In website/index.html around lines 23 (and also lines 40-41), both CTA anchors
point to a non-existent "#download" anchor; fix by either adding a corresponding
download section with app store links and an id="download" element or update the
hrefs to point to an appropriate destination (e.g., a GitHub releases URL or a
waitlist/signup page). Ensure the chosen destination is live, update all CTA
links consistently, and if adding a section include clear app store buttons and
an id attribute matching the CTA href.

</div>
<button class="menu-toggle" aria-label="Toggle Menu">
<span></span>
<span></span>
<span></span>
</button>
</div>
</nav>

<header class="hero">
<div class="container hero-content">
<span class="hero-badge">Open Source & Decentralized</span>
<h1>Split Expenses,<br><span>Not Relationships.</span></h1>
<p>The first privacy-focused, offline-first alternative to expense sharing. No servers, no tracking—just you
and your friends syncing directly.</p>
<div class="hero-btns">
<a href="#download" class="btn btn-primary">
Get Early Access
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round">
<line x1="12" y1="5" x2="12" y2="19"></line>
<polyline points="19 12 12 19 5 12"></polyline>
</svg>
</a>
<a href="https://github.com/Jitmisra/Zplit" target="_blank" class="btn btn-secondary">
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent GitHub repository URL.

The "View Source" link points to https://github.com/Jitmisra/Zplit, but the PR description and footer links (line 181) reference https://github.com/StabilityNexus/Zplit. Ensure the correct repository URL is used.

Apply this diff if StabilityNexus is the correct organization:

-                <a href="https://github.com/Jitmisra/Zplit" target="_blank" class="btn btn-secondary">
+                <a href="https://github.com/StabilityNexus/Zplit" target="_blank" class="btn btn-secondary">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<a href="https://github.com/Jitmisra/Zplit" target="_blank" class="btn btn-secondary">
<a href="https://github.com/StabilityNexus/Zplit" target="_blank" class="btn btn-secondary">
🤖 Prompt for AI Agents
In website/index.html around line 48, the "View Source" anchor href points to
https://github.com/Jitmisra/Zplit which is inconsistent with other references;
update the href to the correct repository URL
(https://github.com/StabilityNexus/Zplit) so all GitHub links match, and verify
target and class attributes remain unchanged.

<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"
xmlns="http://www.w3.org/2000/svg">
<path
d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.04-.015-2.04-3.338.72-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 21.795 24 17.31 24 12c0-6.63-5.37-12-12-12" />
</svg>
View Source
</a>
</div>
</div>
</header>

<section id="features" class="features">
<div class="container">
<div class="section-header">
<h2>Why Zplit?</h2>
<p>Designed for absolute privacy and seamless local interaction.</p>
</div>
<div class="feature-grid">
<div class="feature-card">
<div class="icon-box">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
</svg>
</div>
<h3>Decentralized Core</h3>
<p>No central servers means no data leaks. Your financial data lives on your device and syncs
directly with trusted peers.</p>
</div>
<div class="feature-card">
<div class="icon-box">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18.36 6.64a9 9 0 1 1-12.73 0" />
<line x1="12" y1="2" x2="12" y2="12" />
</svg>
</div>
<h3>Offline First</h3>
<p>Designed for the real world. Sync expenses instantly via WiFi Direct, Bluetooth, or NFC—even
without internet.</p>
</div>
<div class="feature-card">
<div class="icon-box">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3" />
<path
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" />
</svg>
</div>
<h3>Total Privacy</h3>
<p>We don't sell your data because we don't have it. Data is end-to-end encrypted and stored
primarily on your device.</p>
</div>
<div class="feature-card">
<div class="icon-box">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<polyline points="14 2 14 8 20 8" />
<line x1="16" y1="13" x2="8" y2="13" />
<line x1="16" y1="17" x2="8" y2="17" />
<polyline points="10 9 9 9 8 9" />
</svg>
</div>
<h3>on-Device OCR</h3>
<p>Scan receipt images instantly. Our lightweight AI models run locally on your phone to parse
totals and dates.</p>
</div>
<div class="feature-card">
<div class="icon-box">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M22 12h-4l-3 9L9 3l-3 9H2" />
</svg>
</div>
<h3>Visual Analytics</h3>
<p>Gain insights into your spending habits with privacy-preserving local charts and graphs built
with FL Chart.</p>
</div>
<div class="feature-card">
<div class="icon-box">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2" />
<line x1="8" y1="21" x2="16" y2="21" />
<line x1="12" y1="17" x2="12" y2="21" />
</svg>
</div>
<h3>Cross Platform</h3>
<p>Built with Flutter for a seamless experience on both Android and iOS devices, with future web
support.</p>
</div>
</div>
</div>
</section>

<section id="workflow" class="workflow">
<div class="container">
<div class="section-header">
<h2>How it Works</h2>
<p>No signups, no clouds. Just instant collaboration.</p>
</div>
<div class="workflow-grid">
<div class="workflow-item">
<div class="step-number">1</div>
<h3>Create a Group</h3>
<p>Start a local group for your trip or event. No accounts needed, just a local profile.</p>
</div>
<div class="workflow-item">
<div class="step-number">2</div>
<h3>Scan & Split</h3>
<p>Snap a photo of the bill. AI extracts the items. You tap to assign costs.</p>
</div>
<div class="workflow-item">
<div class="step-number">3</div>
<h3>Sync Instantly</h3>
<p>Tap phones (NFC) or scan a QR code to sync the group state with friends nearby.</p>
</div>
</div>
</div>
</section>

<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-brand">
<h3>Zplit<span class="dot">.</span></h3>
<p>The open-source, decentralized standard for expense sharing.</p>
</div>
<div class="footer-links">
<h4>Project</h4>
<a href="https://github.com/StabilityNexus/Zplit">GitHub Repository</a>
<a href="https://github.com/StabilityNexus/Zplit/issues">Contribute</a>
<a href="#">Documentation</a>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Broken documentation link.

The "Documentation" link in the footer points to #, which is a placeholder. Either remove this link or update it with the correct documentation URL.

🤖 Prompt for AI Agents
In website/index.html around line 183, the footer's "Documentation" anchor
currently points to a placeholder href="#" which is broken; update the anchor to
either remove the link entirely or replace the href with the correct
documentation URL (e.g., /docs or the external docs site) and ensure the anchor
uses a valid absolute or relative URL and includes target="_blank" and
rel="noopener" if it opens externally.

</div>
<div class="footer-links">
<h4>Community</h4>
<a href="https://discord.gg/YzDKeEfWtS">Discord Server</a>
<a href="https://t.me/StabilityNexus">Telegram Group</a>
<a href="https://x.com/StabilityNexus">Twitter / X</a>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2025 Stability Nexus. All rights reserved.</p>
</div>
</div>
</footer>

<script src="script.js"></script>
</body>

</html>
37 changes: 37 additions & 0 deletions website/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
document.addEventListener('DOMContentLoaded', () => {
const menuToggle = document.querySelector('.menu-toggle');
const navLinks = document.querySelector('.nav-links');
Comment on lines +2 to +3
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add null check for navLinks before using it in the event handler.

If .nav-links doesn't exist in the DOM, navLinks will be null, and the code inside the click handler (line 7) will throw an error when trying to access navLinks.style.display.

Apply this diff:

     const menuToggle = document.querySelector('.menu-toggle');
     const navLinks = document.querySelector('.nav-links');

     if (menuToggle) {
         menuToggle.addEventListener('click', () => {
+            if (!navLinks) return;
             navLinks.style.display = navLinks.style.display === 'flex' ? 'none' : 'flex';

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In website/script.js around lines 2 to 3, navLinks may be null if the .nav-links
element is absent; update the click handler to guard against that by checking
navLinks before accessing its style (e.g., return early or skip the toggle when
navLinks is falsy) so the event handler safely no-ops when the element is
missing.


if (menuToggle) {
menuToggle.addEventListener('click', () => {
navLinks.style.display = navLinks.style.display === 'flex' ? 'none' : 'flex';
if (navLinks.style.display === 'flex') {
navLinks.style.flexDirection = 'column';
navLinks.style.position = 'absolute';
navLinks.style.top = '70px';
navLinks.style.left = '0';
navLinks.style.width = '100%';
navLinks.style.backgroundColor = 'white';
navLinks.style.padding = '20px';
navLinks.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)';
}
});
Comment on lines +6 to +18
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Refactor to use CSS classes instead of inline styles.

Setting inline styles in JavaScript (lines 7-16) makes the code harder to maintain and overrides CSS specificity. The white background on line 14 also conflicts with your dark theme.

Consider adding a CSS class in styles.css:

.nav-links.mobile-open {
    display: flex !important;
    flex-direction: column;
    position: absolute;
    top: 70px;
    left: 0;
    width: 100%;
    background-color: var(--surface-color);
    padding: 20px;
    box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}

Then simplify the JavaScript:

-        menuToggle.addEventListener('click', () => {
-            navLinks.style.display = navLinks.style.display === 'flex' ? 'none' : 'flex';
-            if (navLinks.style.display === 'flex') {
-                navLinks.style.flexDirection = 'column';
-                navLinks.style.position = 'absolute';
-                navLinks.style.top = '70px';
-                navLinks.style.left = '0';
-                navLinks.style.width = '100%';
-                navLinks.style.backgroundColor = 'white';
-                navLinks.style.padding = '20px';
-                navLinks.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)';
-            }
+        menuToggle.addEventListener('click', () => {
+            navLinks.classList.toggle('mobile-open');
         });
🤖 Prompt for AI Agents
In website/script.js around lines 6 to 18, the click handler sets multiple
inline styles (lines 7–16) which is hard to maintain and conflicts with theming;
remove those inline style assignments and instead toggle a CSS class (e.g.
"mobile-open") on the navLinks element. Add the class definition to styles.css
with the mobile layout rules (display:flex, flex-direction:column,
position:absolute, top:70px, left:0, width:100%, padding:20px, box-shadow) and
use background-color: var(--surface-color) instead of white; ensure base CSS
hides nav links on mobile and the new class forces display for open state (use
specificity or !important only if necessary). Finally, update the JS handler to
only toggle navLinks.classList.toggle('mobile-open') and remove all inline style
changes.

}

// Smooth scrolling for anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth'
});
// Close mobile menu if open
if (window.innerWidth <= 768 && navLinks.style.display === 'flex') {
navLinks.style.display = 'none';
}
}
});
});
});
Loading