Repository navigation
Launch billy: bring the product into main - #47
Merged
Merged
Conversation
…al, and modern green styles - Introduced CleanPage component for a clean invoice layout. - Added CorporateBluePage component with a corporate blue theme. - Created MinimalTealPage component featuring a minimalistic teal design. - Developed ModernGreenPage component with a modern green aesthetic. fix: update plans and access control for templates - Updated PAID_TEMPLATES and FREE_TEMPLATES to include new templates. - Enhanced isTemplateAccessible and canCreateInvoice functions to check paywall status. feat: implement rate limiting functionality - Added rate limit checks to prevent excessive requests per user. - Implemented periodic cleanup of stale rate limit entries. feat: integrate PostHog for analytics tracking - Created posthog-client module for user identification and event tracking. fix: enhance security headers in proxy responses - Added security headers to responses in the proxy function. chore: database schema updates for security and analytics - Added updated_at columns and triggers to profiles and invoices tables. - Introduced analytics tracking columns to profiles for signup source and first invoice date. - Aligned subscriptions table with Dodo Payments identifiers.
The GET /api/invoices endpoint was fetching all invoices from all users
instead of filtering by the current user, causing the dashboard to load
1.2 MB of data and take 34+ seconds. Adding the missing .eq("user_id", user.id)
filter reduces this to kilobytes and sub-second response times.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Introduced two new invoice templates: Mono Classic and Mono Elegant. - Updated the PDF generation logic to support the new templates. - Added functionality to upload and manage invoice logo/stamp images in a dedicated storage bucket. - Implemented a migration script to transfer existing base64 images to the new storage system. - Enhanced the theme configuration to include styles for the new templates. - Updated the plans to include the new templates as free options. - Added security headers to the proxy response.
…cs and readability; add MorphingSquare component for enhanced UI interactions
- Deleted DotFlowCta component and its associated logic. - Added InvoiceMock component to display a sample invoice layout. - Updated LandingCta component to remove InteractiveHoverButton and simplify the button structure. - Removed FlickeringGrid and InteractiveGridPattern components to streamline UI. - Deleted InteractiveHoverButton component as it was no longer in use. - Introduced DodoPayments integration in a new lib/dodo.ts file. - Updated package.json to include dodopayments dependency.
- Changed selection background color in globals.css for better visibility. - Refactored invoice field styles to improve hover effects and transitions. - Introduced new app input and button styles for a consistent UI across components. - Replaced anchor tags with Link components for navigation in the footer. - Enhanced OptionsPanel with accessibility attributes and improved styling. - Added a new CustomizePanel component for invoice customization options. - Removed deprecated currency-select and template-selector components. - Updated toolbar to include a button for toggling the CustomizePanel. - Cleaned up unused InvoiceSettingsPanel component and related imports. - Adjusted editable number and text components for consistent styling and focus effects.
…GN.md (#7) * chore: restructure into turborepo monorepo (apps/web + packages) Moves the Next.js app into apps/web and extracts shared code into packages/invoice-core, packages/design and packages/typescript-config. Adds root DESIGN.md, docs, CI workflows and migration 00009. * feat(editor): redesign invoice editor around a single page + inspector The editor rendered the same InvoicePage twice (editable canvas + identical read-only "PDF Preview"), hid all settings behind a Customize drawer, and drifted from root DESIGN.md (magenta selection, slate/indigo accents, all-caps tracked labels, teal/rose dialog). Layout - One centred A4 page on a muted desk; the duplicate preview pane and the resizable split are gone. - Persistent left inspector: amount-due summary (total, due-in-N-days, client, item count), visual template gallery, currency, quick-setup presets with active state, field switches, and a tax section. - Below lg the inspector becomes a slide-over drawer (Esc/backdrop close). UX - Edit / Preview segmented switch (Cmd/Ctrl+E). Preview renders the page read-only, which is exactly what the PDF exporter rasterises. - Floating zoom bar: -/+ steps, click % for 100%, Fit to width. - Cmd/Ctrl+P downloads the real PDF instead of the browser print dialog; button shows a busy state; success/failure via toasts instead of alert(). - "New" uses a confirm dialog (explains what is kept) instead of window.confirm. - Autosave status in the top bar ("Saved on this device"). - Editable fields show a faint dotted baseline while hovering the sheet and a blue wash on hover; first-run "click any text to edit" tip (dismissible, remembered). Edit-only affordances never render in preview/PDF. - GST: CGST+SGST vs IGST is one mutually exclusive choice (matching how totals already compute) instead of three independent toggles. - Full-width dashed "Add line item" row. Design system - New .ed-* chrome layer in globals.css (@layer components): white surfaces, #37352f text, #E5E7EB hairlines, #F3F4F6 wash, #0070D7 as the only chroma, 7px/8px radii, Inter, natural-case labels. - .app-option / .app-kbd selection switched from magenta to blue; field focus ring unified on #0070D7; confirm dialog restyled. Also fixes the dev-overlay hydration warning caused by the pre-hydration data-js script on <html> (suppressHydrationWarning on that element only). * fix(editor): reserve the page's full border-box height on the desk ResizeObserver contentRect excludes the sheet's padding and border, so the scaled wrapper was ~98px shorter than the page and the bottom of the invoice spilled past the reserved space. Measure offsetHeight (unscaled border box) instead.
* feat(landing): bright Tally-style redesign, drop the navy gradient panels
The landing page was built from stacked navy gradient panels whose
top-left stop (rgba(164,178,235,.5)) washed the navy toward lavender, plus
a glass orb, dark gradient bento tiles, two-tone grey headlines and
ALL-CAPS eyebrows. Rebuilt around root DESIGN.md ("Tally Minimal Forms").
- White canvas, #37352f ink everywhere (hierarchy by size/weight), Inter
700 headlines, #0070D7 only on CTAs and emphasis marks, #E5E7EB
hairlines, 7/8/12px radii, natural-case section kickers.
- Hero: centered promise with a hand-drawn blue underline that draws
itself, sparkle/coin doodles, 40/48px buttons (not pills), and the live
typing invoice as a large browser-framed product shot with hand-drawn
annotations ("Click a line and type", "Totals update themselves").
- Trades as muted chips (replaces the marquee).
- How it works: three numbered steps, each with a small proof: a focused
field with caret, the six real template palettes from invoice-core, and a
ready PDF file.
- Privacy: argument + big-numeral facts in hairline rows (replaces the dark
stats panel).
- Built for: four open columns with sketch icons and blue checks, no cards.
- FAQ: sticky heading column + hairline accordion; adds a GST answer.
- Closing: muted band with paper-plane doodle (replaces the dark CTA panel).
- 404 restyled to match.
Reuses the parked components/landing/doodles.tsx (paths opt into the
draw-on animation via pathLength). Removes the marquee, orb, tilt parallax
and dark-button tone. Reduced motion disables every draw/reveal animation.
* feat(landing): animated SVG scenes for "How it works"
Replace the three static illustration wells with looping SVG scenes built
on motion (already a dependency):
1. Click anything and type: a cursor clicks a line, the field takes a blue
focus ring, text types in behind a moving caret, then the amount lands.
2. Pick a look: a mini invoice recolours while a selection ring steps
across four real template palettes from invoice-core (Clean, whose
primary is "transparent", shows its accent).
3. Download the PDF: the sheet folds into a PDF file, the download arrow
draws into a tray, and a check confirms it is ready.
Each scene is one keyframe timeline on a shared clock, so its parts stay
in lock-step with no JS scheduler. Scenes animate only while on screen,
and rest on a static finished frame off screen and under
prefers-reduced-motion.
The Private-by-design stamp now draws when it scrolls into view
(DrawOnView). Reveal's 2.2s backstop had it finish before anyone got
there. Also fixes the pen-draw CSS: the build lowercased
path[pathLength] into a selector that never matches SVG, so drawable
paths are marked with data-draw instead.
Closes #9
* feat(landing): polish pass: measured annotations, richer scenes, micro-interactions
Hero annotations
- The arrows were placed with guessed offsets and hidden below 1320px, so
most laptops saw none and wide screens saw arrows pointing near, not at,
their targets. HeroAnnotations now measures the typed row and the total
and pins a numbered marker on the frame edge level with each. Wide
screens get a label with a hand-drawn arrow (drawn on in view) landing
on the pin. Narrower screens get a numbered legend under the frame. The
row pin pulses while typing.
Step scenes
- Pick a look: a cursor now does everything the copy promises. It picks a
template (the header band sweeps in), uploads a logo (it pops in) and
switches currency (the total rolls from $2,300 to ₹2,300).
- Download the PDF: a real flow. The cursor clicks Download, a progress
bar fills, the invoice flies into a Downloads panel and lands as a file
with a check, a "1" badge and "Saved to your device".
- Fix: several timelines ended before 1.0. Motion ends a repeat at the last
keyframe, so those elements looped early and drifted out of sync. All
timelines now span the loop, with a dev-mode warning if one doesn't.
New scene
- Private by design: data orbits inside a browser tab ("this tab only"),
and a request heading for a server bounces off a barrier. It replaces
the static stamp, which retires DrawOnView.
Micro-interactions (all off under prefers-reduced-motion)
- Hero sparkles twinkle and the coin flips. The closing paper plane
glides with a moving trail.
- Hover: chips lift, step numbers fill, use-case icons wiggle, and
buttons get a pressed state.
Closes #15
* fix(editor): avoid hydration mismatch in the amount-due summary /editor is statically prerendered, but the summary formatted the due date with the visitor's locale and computed "Due in N days" from today during render, so server and client text differed and React threw. Render those parts only after hydration (useSyncExternalStore) and show the ISO date until then. Closes #11 * feat(editor): animated switches, tax radio group and Edit/Preview tabs Replace three hand-rolled editor controls with motion components adapted from beUI (MIT, https://beui.dev). The source is copied into components/motion/ with the original license, restyled to root DESIGN.md (the shadcn primary token is black here, not blue): - Switch: spring-driven thumb for every inspector field toggle. SwitchRow is now a <label> for the switch, so the whole row still toggles it, and descriptions are wired up with aria-describedby. - RadioGroup: gliding indicator dot for Single tax / CGST+SGST / IGST, with a description per option and arrow-key navigation (roving tabindex). - Tabs (segment): the Edit/Preview toggle becomes a gliding white pill with a blue clip-masked active label. aria-label/title passthrough keeps the icon-only triggers labelled on small screens. The old .ed-segment and .ed-switch CSS is removed. Every component respects prefers-reduced-motion. No new dependencies: motion, clsx, tailwind-merge and lucide-react were already installed. Sidebar/File tree were considered and left out: Billy keeps a single local draft, so there is nothing for them to list yet. Closes #10
…14) * feat(editor): command palette, undo/redo, payment terms, invoice numbering Make the editor faster to drive without adding visible chrome. - Command palette (Cmd/Ctrl+K): a searchable list of every editor action. Add a line item (then start typing its description), download, preview, new invoice, undo/redo, payment terms, field toggles, tax type, template and currency. Rows show their shortcut and current value, so the palette doubles as the shortcut reference. WAI-ARIA combobox, keyboard driven. Field toggles keep it open so several can be flipped in a row. - Undo/redo (Cmd+Z, Shift+Cmd+Z, Ctrl+Y): bounded history (100 steps) wrapped around the invoice reducer. Each committed field edit is one step. The initial load from localStorage resets history, and the shortcuts stand aside while a text field is focused so native text undo still works. - Payment terms: On receipt / Net 7 / Net 15 / Net 30 quick picks in the inspector set the due date from the issue date, and the current term is highlighted. - Invoice numbering: New invoice bumps the number and keeps prefix and zero padding (INV-1024 -> INV-1025, 2026/009 -> 2026/010). - The first-run tip mentions Cmd+K. No new dependencies. Closes #13 * feat(editor): make commands, undo/redo and payment terms visible Reviewer feedback: the palette, undo/redo and payment terms were keyboard-only or below the fold, so the editor looked unchanged. - Toolbar: a "Commands" launcher with its Cmd+K hint (icon-only below xl), plus undo/redo buttons that dim when there's nothing to undo or redo. All three carry aria-keyshortcuts. - Payment terms move up to sit directly under the amount-due summary, next to the due date they change. Refs #13 * style(editor): keep payment-term chips on one line
* feat: use Rune Icons for every UI icon Replace lucide-react, @hugeicons and hand-drawn inline icons with Rune Icons (Apache 2.0, https://github.com/Nexvyn/runeicons, outline style). - Rune isn't on npm yet, so the 28 glyphs Billy uses are vendored in components/icons/rune.tsx with the license in components/icons/LICENSE-RUNE. Each SVG was checked to contain only plain shapes (no scripts, links or styles). Strokes use currentColor, and a `size` prop, overridable strokeWidth and optional `title` are provided (aria-hidden by default). - Swapped: the editor toolbar, canvas, inspector, command palette, line items, logo and stamp upload, the beUI tabs' scroll chevrons, the select, toasts, the landing CTA arrow and the use-case icons. - Close substitutes where Rune has no exact glyph: rotate-ccw/cw for undo/redo, refresh-cw as the spinner, cloud for "saved", file-down for Download PDF, and shield-check for the stamp placeholder. - Toasts: badge colours move from emerald/rose/teal to DESIGN.md (blue, #D92D20 for errors), and the dismiss button gets an accessible name. - Remove lucide-react, @hugeicons/core-free-icons and @hugeicons/react. Landing doodles (sparkles, coin, arrows, plane) are illustrations and stay. Refs #18 * fix(pdf): render the export root synchronously React 19's createRoot().render() is concurrent, and a single animation frame wasn't always enough for it to commit on a cold or slow page. The export then threw "Invoice failed to render for export", which headless testing hit on the first attempt after load. Render with flushSync so the invoice DOM exists before measuring, then wait one frame for pagination's layout effects. Refs #19
… (#27) #22, #24 and #26 were squash-merged into their own base branches (feat/rune-icons, feat/landing-bento, feat/editor-polish) rather than into launch-alpha, so only #20 landed here. Nothing was lost: the combined work sits on feat/editor-polish. This commit carries the exact tree of feat/editor-polish, verified identical to feat/brand, so launch-alpha ends up with what the four PRs reviewed together: - #22 Landing: bento sections, scene cards, feature tiles, footer band, trust chips with tooltips - #24 Editor: discount-before-tax fix with tests, template-driven colours, no placeholders in the preview/PDF, four new templates - #26 Brand: mark, wordmark, favicon, OG image; every template fits one page
Both hosts build from the repo root, where package.json has no "next" dependency, so Vercel failed with "No Next.js version detected" and never got as far as the app in apps/web. Billy is a static export, so neither host needs a framework preset: build from the root with turbo and publish apps/web/out. - vercel.json: framework null, bun install, bun run build, output apps/web/out, and the security headers from apps/web/public/_headers (which only Cloudflare Pages reads). github.silent stops the bot commenting on every push. - netlify.toml: the same build and publish settings, pinned to the repo's Bun version, with the same headers. Verified with a clean root build: `bun install && bun run build` emits apps/web/out with index.html, editor.html, 404.html, the brand assets and the icons. Refs #17
A fresh clone of launch-alpha fails `bun run build`: next's type check reaches apps/web/scripts/migrate-images-to-storage.ts, which imports @supabase/supabase-js — a package that is in no package.json and appears zero times in bun.lock. Local checkouts only built because they still had it in node_modules from before the backend was dropped, so the failure only showed up on Vercel. The script is one of the dead Supabase leftovers AGENTS.md lists, so exclude that one file from the app's type check rather than resurrect the dependency. download-fonts.ts stays type-checked. Also set cleanUrls, since /editor exports as editor.html rather than editor/index.html and vercel.json now serves apps/web/out as plain static files. Refs #17
Two changes to the same two files, so they land together. The "Free during alpha / No account / Nothing uploaded" pills under the final CTA repeated the hero chip and three of the six FAQ answers, and the tooltips hid the reasons behind a hover nobody has on touch. The section closes on the two buttons now. The trades strip was a centred cloud of grey pills that read as filler. It is now a band: a blue wash with hairline rules top and bottom, the heading and a line of copy on the left, and the ten trades as a numbered index on the right. The band also gives the page its first break from white, which is what made the old strip disappear. Two columns of five on desktop, one column below 520px, and the heading stacks above the index below 860px.
The flat blue tile with a stroked stem-and-ring is now a glass tile: a vertical gradient through #0070D7, a sheen along the top edge and a rim light that is bright at the top and fades out. The "b" itself is solid — a rounded stem and a filled bowl — so it still reads at 16px, and the tile stays blue rather than near-black so it survives a dark tab strip. Chroma stays blue-only per DESIGN.md. In the app the gradients are CSS (.brandmark) rather than SVG: the mark renders in the nav, the footer, the editor top bar and the 404, and duplicated SVG gradient ids break whichever instance loses. Standalone files keep their own gradients: app/icon.svg, brand/mark.svg and brand/logo.svg. app/apple-icon.png is redrawn at 180x180, full-bleed and opaque, because iOS masks the corners itself and fills transparent ones with black. brand/og.png is regenerated with the new mark.
Geist replaces Inter on the landing page, the editor chrome and the wordmark. It is loaded through next/font/google alongside Geist Mono, which the editor already used. Only interface declarations move. Invoice templates each name their own family and resolve it through --invoice-font, and the Clean template reads var(--font-sans), so none of the template variables are touched: the document renders glyph-for-glyph as before and no page break moves. Verified by diffing the editor canvas before and after. DESIGN.md is the spec of record and said Inter, so it moves with the code, with a note that templates are deliberately exempt. brand/logo.svg and brand/og.png are regenerated in Geist.
The sixteen tiles used general-purpose Rune icons, and general-purpose icons can only answer "GSTIN", "HSN / SAC codes" or "reverse charge" with a near-miss. Sixteen near-misses in one grid is what made it read as stock: a fingerprint for GSTIN, a laptop for "works offline", and a cloud for autosave in a product whose whole claim is that nothing is uploaded. components/icons/billy.tsx draws all sixteen on Rune's 24-unit grid with the same caps and joins, so the two sets sit together: a barcode for HSN codes, a bank front for bank details, the command loop for ⌘K, the generic currency sign for "10 currencies", a struck-through cloud for "works offline", an arrow into a tray for autosave. Rune stays the set for the editor's UI verbs. The tinted rounded square behind each icon is gone — sixteen identical blue chips were most of the stock look — and the glyph goes from 18 to 21px now that it carries the tile on its own.
Cloudflare's Workers Builds flow runs `npx wrangler deploy`, which needs a config in the repo. There was none: the project predates the move off the Pages flow. Billy is a static export, so this is an assets-only Worker — no `main`, no Worker code, Cloudflare serves apps/web/out directly. html_handling is the part that matters: Next exports /editor as editor.html rather than editor/index.html, so without "auto-trailing-slash" the editor link 404s on a build that otherwise looks healthy. out/_headers keeps working — Workers parses it the same way Pages did. Validated with `wrangler deploy --dry-run`: 132 files read from the assets directory.
AGENTS.md said Cloudflare Pages. The project deploys through Workers static assets now, configured by wrangler.jsonc at the root, from the launch-alpha branch.
The mark was a glass tile with a geometric "b". It is now Billy: a slightly tilted torn receipt with two eyes and two total lines, on a sky-blue tile with soft light streaks. It still reads as a face at 16px. The tile gradient stays in CSS (.brandmark) so the mark can render several times on a page without duplicate SVG gradient ids. The rim light moves to ::after so it sits over the receipt. The standalone assets match the new mark: app/icon.svg, app/apple-icon.png (180x180, full-bleed, opaque), brand/mark.svg, brand/logo.svg and brand/og.png. Refs #28
You could only tell a field was editable by hovering it: the dotted baseline appeared on canvas hover and the tint on field hover, so a first-time user reading the invoice saw a static document. Every editable field now carries a visible hairline at rest, and the computed cells — amount, subtotal, total — deliberately do not, so the outline reads as "you can change this". Two things make this safe for the PDF: - The 1px border was already reserved as `solid transparent`, so giving it a colour changes no geometry. Nothing moves, no page break shifts. - `.invoice-field` is only emitted when the field is editable. Preview and the export render through RenderModeProvider readOnly, which returns plain markup without the class, so none of this can leak into a PDF. The print rules already blank the border too. The colour is mixed from currentColor rather than fixed: Studio, Letterhead, Modern Green and Corporate Blue put white text on a coloured band, where a grey or blue hairline would disappear. Their hover borders are matched to white for the same reason. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One 340px rail held the template gallery and every setting, so picking a look and filling in details competed for the same column. Templates now sits on the left and setup on the right, each with its own header and collapse control. Collapsed, a rail becomes a 44px strip on the same edge with a vertical label, so the way back in stays where the rail was instead of moving to the top bar. Both are also togglable from the palette, under a new Panels group. Widths are 260 and 300 so both can be open at 1440 and still leave the desk enough room for the A4 page at 100%. At 1280 with both open the page fits to about 0.83. Open/closed is remembered per browser under its own localStorage keys, never in billy_invoice_data — it is a preference about the chrome, not invoice data. usePersistedFlag follows the canvas hint's pattern: server and first client render both use the default and the stored value is adopted after hydration, so this cannot reintroduce the prerender mismatch from #11. Templates defaults closed, since you pick one once. Below lg there is still one drawer, and it now carries the template gallery as well as setup — on a narrow screen it is the only way to reach it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The palette was a flat list of labels on a plain scrim. It now blurs what is behind it, so the invoice drops away while you are choosing. Two notes on that one line: the value is a single filter function because the CSS minifier joins `blur(8px) saturate(115%)` into `blur(8px)saturate(115%)` and the declaration is then dropped — the computed style read back `none` until this was split out. Both the prefixed and standard properties survive minification now, confirmed in the built stylesheet and by reading getComputedStyle in the page. The rest is the list itself: every row carries a leading glyph on a shared 24px axis, and template rows show their own three colours instead of an icon, so a look is visible before you run it. Group headers are uppercase and quieter, the active row gains an ↵, the search line shows how many commands match, and the panel is wider with a softer corner and a spring entrance. use-editor-commands is now .tsx, since the icons are JSX. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The active palette row carried both a blue wash and a 2px bar down its left edge. The wash already marks it, so the bar is gone. A collapsed rail no longer turns its name sideways. It is a slim tinted strip with one icon button and a muted chevron pointing the way the rail will open, with the name on the button's tooltip and label instead — a rotated word is hard to read and made the strip look like a leftover. While the templates rail was open it was also truncating half its names to "Corporate Bl…" and "Mono Eleg…", because the name shared its row with the selected tick. The tick is now a badge on the thumbnail and the Pro chip sits beside it, so the name gets the full cell width; with the rail at 280px all ten fit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Editor - On a touch screen where the page is scaled below 75% (or a phone held sideways), tapping a field opens a bottom edit bar: a 16px input above the keyboard, labelled with the field, with Done and Cancel. The page itself is untouched, so the preview and PDF render exactly as before. - Number fields use a decimal keypad and accept "12,50" as 12.5. - The first-run tip says "Tap any text to edit it." on touch and wraps instead of pushing the page off screen at 320px. - Undo and redo move into the bottom zoom bar on phones, where the top bar has no room for them. - The command palette hides keyboard hints on touch screens. - The invoice number in the top bar shows from 1024px up, instead of truncating to "I..." on tablets. Landing - The hero headline scales down on narrow phones instead of overflowing. - The "Designed for you" checklists stack to one column under 560px. - The hero legend stacks and gets room below the product shot. - The "Invoices this month" label no longer runs out of its card. Checked at 320, 375, 390, 768, 1024 and 1280px wide. tsc is clean, lint still reports the same 4 errors and 12 warnings (none in these files), and the static build passes. There is no test runner. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Billy launches free but not open source, so the landing page no longer links to GitHub or calls itself open source: - Remove the "View the source" button and the footer's Open source column (GitHub, Report a bug, Issues). - Reword the hero note, footer blurb, Unlimited card and the "Is it really free?" answer to say free, with every feature unlocked. Also remove the hero chip, the numbered legend under the product shot (and its pins on narrow screens) and the footer lock line. Replace em-dashes in visible text: page titles use "Billy | ...", two FAQ answers use commas, and an empty client name in the settings panel reads "Not set". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The default invoice hardcoded "INV-1024", so every first invoice on a new
browser, device or cleared storage got the same sample number. "New" also
fell back to INV-1024 whenever the current number had no trailing digits,
silently reusing a number.
- New drafts start at INV-<year>-001 (unique per year, as GST expects).
- "New" still bumps the trailing number and keeps prefix and padding.
- A number with no digits gets a suffix ("Draft" -> "Draft-2"); an empty
one starts a new sequence.
- Numbering moves to @billy/invoice-core (numbering.ts) with tests.
Existing saved drafts keep their current number.
* feat: founding members (numbers, Founders template, wall) Everyone on the waitlist when billy launches becomes a founding member: a number by signup order, a personal link that unlocks a founders-only template, and a spot on a public wall if they asked for one. There is still no server, account or database. - invoice-core/founders.ts: codes look like v1.<n>.<signature>, signed offline with an Ed25519 key and checked in the browser against the public key. Nobody can mint one or move it to another number. - Editor: opening /editor#founder=<code> verifies it, remembers it in this browser, strips it from the URL and says hello. A bad link gets a clear error instead of silently doing nothing. - Founders template: billy's sky-blue gradient masthead, white type and the landing page's hand-drawn underline. Locked in the template rail for everyone else and hidden from the command palette. Exports to PDF. - /founders: the founding members wall, from content/founders.json (name, number, avatar only; never an email), with Gravatar photos or initials, and a "you're #n" line for founders. Linked in the footer. - scripts/founders: keygen (once; private key lives in ~/.billy, never in the repo) and launch (reads the Loops export, numbers everyone, signs links, writes founderNumber/founderLink back to Loops, builds the wall, keeps a private record in ~/.billy). * docs: add the founders + launch runbook A single reference doc so the launch-day process, file map, and email template survive even if context gets lost. Covers what's done, what's left, the exact commands to run on launch day, and the recovery notes for the signing key. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * rename: founding member -> early bird; avatars via blobatar.dev "Founding member" read as too much. User-facing copy now says "early bird" throughout (template name, unlock toasts, the wall, docs/founders-launch-plan.md and its email template). Internal plumbing is untouched on purpose: Loops properties (founderNumber/ founderLink), the localStorage key, the #founder= URL param, file and route names, and the signing message all stay as they are — renaming those would mean redoing Loops setup and invalidating nothing-yet-issued links for no user-visible benefit. Also swap Gravatar for blobatar.dev for wall avatars: a deterministic generated avatar per email, no key, no account, no dependency on whether someone happens to have a Gravatar set up. Everyone who opts into the wall now gets a consistent on-brand avatar. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: bump status note (retrigger Cloudflare build) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* chore: license billy under MIT Same LICENSE as the one going onto main, so the product branch carries it too. Refs #35 * feat(landing): real editor screenshot in the hero, Star on GitHub The hero now shows the actual /editor (seeded with Acme Studio billing Maple & Co.) instead of the hand-built LiveInvoice mock, as webp in the existing browser frame. Phones get a crop of the mobile editor. The hand-drawn pins still point at a real line item and the amount due. The typing animation and its CSS are gone with the mock. Nav gains a Star on GitHub button with the live count (icon only on phones), and the footer an Open source column: GitHub, Contribute, MIT license. Refs #37, #38 * docs: third-party notices for launch-alpha Refs #35 * fix(landing): recapture the hero shot without the floating zoom control The zoom pill sat on top of "Add line item" and read as a glitch in a still. Also measures the line-item pin instead of estimating it.
…sed code (#43) Removes what nothing references any more: - supabase/ migrations and apps/web/scripts/migrate-images-to-storage.ts (the backend is gone; billy is fully client-side) - netlify.toml, posthog-setup-report.md, skills-lock.json (listed skills this branch doesn't have) - app/_legacy, components/demo, 11 unused components, lib/posthog-client.ts (PostHog is called directly via posthog-js), the Stat component and prefersReducedMotion, create-next-app SVGs in public/ - gsap and @gsap/react, used only by the deleted dot-flow Docs now match: README describes the Cloudflare Workers deploy and the real layout, AGENTS.md and the deep-review skill drop the Supabase and posthog-client references, and AGENTS.md notes invoice-core's bun tests. Refs #39
…r link A founder link only unlocks the browser that opens it, so an early bird on a new device saw a locked template and no way in. Tapping the locked Early Bird template now opens a small dialog: paste the emailed link, or just its code, and the template unlocks on that device. It is the same signature check as the email link, in the browser, and nothing is sent anywhere. extractFounderCode pulls the code out of whatever was pasted, including a link with a line break in it or one a click tracker percent-encoded.
… footer The hero note and the footer blurb no longer say it. The footer credit now reads "Built by Vedant" with a 🫶, still linking to vedantworks.com.
… You card and doodles /founders is now /early-birds. Every early bird is on the wall as their number and a face drawn from it, with a name only for people who gave one, so nobody needs an avatar file or a name to appear. The faces come from the receipt mascot, with enough parts (eyes, mouths, glasses, hats, headphones and so on) that no two numbers match. A browser that holds an early bird's link sees a You card: their own face, a thank you, and an Add my name or Change my name button. There is no server to save a name to, so it opens an email to hello@trybilly.tech and names are added by hand. The launch plan covers handling those requests. The wall also gets doodles, cards that lean and straighten on hover, a count, and a closing call to action. launch.ts now writes every early bird to founders.json (name or null) instead of fetching avatars, and gains --exclude and --only for test signups and test sends.
The tag was on Minimal Teal only. The template itself is unchanged.
Adds the logo (light and dark versions, rendered from the real Brand component because the SVG wordmark depends on the Geist font), screenshots of the landing page, editor, templates, phone layout and early birds wall, and sections for features, privacy, how it works, running it locally, deployment and contributing. The early birds screenshot uses placeholder names.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configuration
⛔ Files ignored due to path filters: 32
📒 Files selected for processing: 254
✨ Finishing Touches
Comment |
❌ Deploy Preview for trybilly failed. Why did it fail? →
|
🚀 Deploying Preview to Cloudflare 🚀Preview Deployments by commit
|
🚀 Deploying Preview to Cloudflare 🚀Preview Deployments by commit
|
❌ Deploy Preview for tryybilly failed. Why did it fail? →
|
Written by scripts/founders/launch.ts from the 2026-10-14 Loops export, with two test and unsubscribed contacts left out. Each entry is a number plus the name the person gave when they gave one (18 of 49). No emails.
… structured data Social cards and canonical URLs now resolve to https://www.trybilly.tech. The og:image was pointing at http://localhost:3000/brand/og.png. robots.txt and sitemap.xml are generated at build time, llms.txt describes the product, and the landing page carries Organization, WebSite, SoftwareApplication and FAQPage data built from the FAQ it already renders. The landing page and the early birds page each get their own canonical URL. These replace the waitlist versions that only exist on main.
Production deploys from main on Vercel, with Cloudflare only on DNS, but the README and AGENTS.md still described a Cloudflare Worker. The README tree and deployment section now match vercel.json, including that the security headers come from there. CONTRIBUTING.md comes over from main, since the landing footer links to it, and now says to branch from main.
main was the waitlist site and launch-alpha is the product, so their files conflict almost everywhere. This records main's history without taking any of its files. What main had that the product still needs (CONTRIBUTING.md and the SEO files) was brought across in the commits just before this one.
vedantlavale
marked this pull request as ready for review
October 14, 2026 14:06
The nav was a flex row with space-between, so the links sat halfway between the logo and the wider button group and drifted left of the page centre. It is now a three-column grid with equal outer tracks, so the links are on the centre. Below 980px, where four links no longer fit centred, they are hidden like they already were on phones. Adds a Sponsor link in the nav, a sponsors section before the final call to action, and a Sponsor billy link in the footer. The button is a mailto to hello@trybilly.tech, as it was on the waitlist site. The sponsors list is empty for now, and logos appear above the button once one is added to lib/sponsors.ts.
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Brings the product (
launch-alpha) intomain, which is currently the live "launching soon" waitlist page, for launch.What's in it
apps/web/early-birds, with a generated mascot face for every early bird, a "You" card and doodlesMerge notes
mainandlaunch-alphahave diverged:mainis the root-level waitlist site,launch-alphais the monorepo.mainintolaunch-alphawith-s ours, which keeps the product tree and recordsmainin the history. Then restore the files that only exist onmainand are still needed (CONTRIBUTING.md, and the SEO files: robots, sitemap,llms.txt, the OG image and the structured data). Then merge this PR with a merge commit, not a squash.mainfor trybilly.tech, so merging puts the product live and replaces the waitlist page. That is why this is a draft.Before merging
scripts/founders/launch.tsand commitapps/web/content/founders.jsonmetadataBaseso link previews don't point atlocalhostCONTRIBUTING.mdNEXT_PUBLIC_POSTHOG_PROJECT_TOKENin the Cloudflare build variables, and check the build output isapps/web/outTesting
bun testinpackages/invoice-corepasses (20 tests).bun run buildpasses and/early-birdsis a static route.bun run lintreports the one existing error inapps/web/scripts/download-fonts.ts. The unlock flow, the paste box and the early birds wall were checked in a browser at phone and desktop widths. The app itself has no unit tests.