Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ body:
attributes:
label: Checklist
options:
- label: I reproduced this on the latest preview revision.
- label: I reproduced this on the latest `main` revision.
required: true
- label: This report does not contain a vulnerability or sensitive data.
required: true
27 changes: 2 additions & 25 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Repository CI

on:
push:
branches: [main, preview]
branches: [main]
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
workflow_dispatch:
Expand All @@ -17,7 +17,7 @@ concurrency:
jobs:
validate:
name: Validate
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 45
steps:
- name: Checkout
Expand Down Expand Up @@ -69,26 +69,3 @@ jobs:
- name: Test
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
run: pnpm test --concurrency=2

storekit-purchase-validation:
name: StoreKit purchase validation
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: macos-15
timeout-minutes: 15
env:
DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22

- name: Test StoreKit transaction retention
run: swift test --package-path libraries/react-native

- name: Test StoreKit purchase and restore
working-directory: libraries/react-native
run: bash ios-storekit-tests/run-tests.sh
4 changes: 2 additions & 2 deletions .github/workflows/osv-scanner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
- selfhost/**/package.json
- .github/workflows/osv-scanner.yml
push:
branches: [main, preview]
branches: [main]
paths:
- package.json
- pnpm-lock.yaml
Expand All @@ -38,7 +38,7 @@ concurrency:
jobs:
scan:
name: Production lockfile
runs-on: ubuntu-latest
runs-on: blacksmith-2vcpu-ubuntu-2404
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/pr-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
# pnpm add @voidhash/node@https://pkg.voidha.sh/node/<short-sha>
#
# - push to main/preview → tarballs tagged with sha + branch name
# - push to main → tarballs tagged with sha + branch name
# - pull_request → tarballs tagged with sha + branch + `pr-<n>` (1 week
# TTL), plus a sticky install-instructions comment on
# the PR
Expand All @@ -19,7 +19,7 @@ name: PR Packages

on:
push:
branches: [main, preview]
branches: [main]
paths:
- packages/generated-clients/**
- packages/shared/**
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/secret-scanning.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Secret scanning

on:
push:
branches: [main, preview]
branches: [main]
pull_request:
schedule:
- cron: "15 11 * * 1"
Expand All @@ -18,7 +18,7 @@ concurrency:
jobs:
scan:
name: Secret scanners
runs-on: ubuntu-latest
runs-on: blacksmith-2vcpu-ubuntu-2404
timeout-minutes: 10
steps:
- name: Checkout full history
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/security-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Security audit

on:
push:
branches: [main, preview]
branches: [main]
paths:
- package.json
- pnpm-lock.yaml
Expand Down Expand Up @@ -37,7 +37,7 @@ jobs:
production-dependencies:
name: Production dependencies
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: ubuntu-latest
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/selfhost.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ on:
push:
branches:
- main
- preview
workflow_dispatch:

permissions:
Expand All @@ -22,7 +21,7 @@ env:
jobs:
smoke:
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 40
steps:
- uses: actions/checkout@v4
Expand Down
50 changes: 50 additions & 0 deletions .github/workflows/storekit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# StoreKit tests need a macOS runner, which bills at a large multiple of the
# Linux rate — so this workflow only triggers when the React Native library
# (the only code these tests exercise) actually changes, instead of on every
# PR. Blacksmith's macOS images mirror GitHub's, so the pinned DEVELOPER_DIR
# stays valid.
name: StoreKit

on:
push:
branches: [main]
paths:
- libraries/react-native/**
- .github/workflows/storekit.yml
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
paths:
- libraries/react-native/**
- .github/workflows/storekit.yml
workflow_dispatch:

permissions:
contents: read

concurrency:
group: storekit-${{ github.head_ref || github.ref }}
cancel-in-progress: true

jobs:
storekit-purchase-validation:
name: StoreKit purchase validation
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: blacksmith-6vcpu-macos-15
timeout-minutes: 15
env:
DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22

- name: Test StoreKit transaction retention
run: swift test --package-path libraries/react-native

- name: Test StoreKit purchase and restore
working-directory: libraries/react-native
run: bash ios-storekit-tests/run-tests.sh
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Supported Versions

Voidhash is currently in private alpha and is not yet supported for production
use. Security fixes are applied to the latest `preview` branch. Older commits,
use. Security fixes are applied to the latest `main` branch. Older commits,
preview builds, and unpublished alpha artifacts are not supported.

This policy will be updated with a version support table before the first
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ function ToolCallBlock({
/**
* The Claude Code pane of the illustration: one user prompt followed by the Voidhash
* MCP calls that compose the paywall. Scrolls itself so the newest call stays in view.
* Fills its container's width; only the height is fixed.
*/
export function AgentTranscript({ playback }: { playback: TranscriptPlayback }) {
const scrollerRef = useRef<HTMLDivElement>(null);
Expand All @@ -105,18 +106,18 @@ export function AgentTranscript({ playback }: { playback: TranscriptPlayback })

return (
<div
className="absolute top-0 left-0 h-129.25 w-99.75 rounded-[16px] border border-zinc-800 border-solid bg-origin-border bg-zinc-950 [box-shadow:#00000040_0px_2px_16px]"
className="relative h-129.25 w-full rounded-[16px] border border-zinc-800 border-solid bg-origin-border bg-zinc-950 [box-shadow:#00000040_0px_2px_16px]"
style={{ backgroundImage: TERMINAL_BACKGROUND }}
>
<div className="absolute top-3 left-3.25 size-3.25 rounded-full bg-zinc-700" />
<div className="absolute top-3 left-8.5 size-3.25 rounded-full bg-zinc-700" />
<div className="absolute top-3 left-13.75 size-3.25 rounded-full bg-zinc-700" />
<div className="absolute top-2.5 left-38.75 font-sans text-xs text-zinc-600 leading-[142.857%]">
<div className="absolute inset-x-0 top-2.5 text-center font-sans text-xs text-zinc-600 leading-[142.857%]">
Claude Code
</div>

<div
className="absolute top-9.75 left-6 h-114.5 w-88.75 overflow-hidden font-mono text-xs text-zinc-400 leading-[142.857%]"
className="absolute inset-x-6 top-9.75 h-114.5 overflow-hidden font-mono text-xs text-zinc-400 leading-[142.857%]"
onScroll={(event) => setScrolled(event.currentTarget.scrollTop > 4)}
ref={scrollerRef}
>
Expand Down
63 changes: 55 additions & 8 deletions apps/www/src/features/www/landing/paywall-build/paywall-build.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"use client";

import { useInView } from "motion/react";
import { useRef } from "react";
import { createContext, type ReactNode, useContext, useRef } from "react";

import { AgentTranscript } from "./agent-transcript";
import { LenscalPaywall, PAYWALL_HEIGHT, PAYWALL_WIDTH } from "./lenscal-paywall";
import { useTranscriptPlayback } from "./use-transcript-playback";
import { type TranscriptPlayback, useTranscriptPlayback } from "./use-transcript-playback";
import { WorkingIndicator } from "./working-indicator";

/**
Expand All @@ -14,11 +14,19 @@ import { WorkingIndicator } from "./working-indicator";
*/
const PAYWALL_SCALE = 0.6015;

const PlaybackContext = createContext<TranscriptPlayback | null>(null);

/**
* The designer illustration's live half: a Claude Code session driving the Voidhash MCP,
* and the paywall assembling itself on the canvas as each call comes back.
* Hosts the shared Claude Code session driving both halves of the paywalls illustration:
* the full-width terminal and the designer canvas the paywall assembles on.
*/
export function PaywallBuild() {
export function PaywallBuildProvider({
className,
children,
}: {
className?: string;
children: ReactNode;
}) {
const ref = useRef<HTMLDivElement>(null);
const started = useInView(ref, { amount: 0.35, once: true });
// The session would otherwise loop (timers, typing interval, pixi ticker) for the rest of the
Expand All @@ -28,13 +36,52 @@ export function PaywallBuild() {
const playback = useTranscriptPlayback(started && visible);

return (
<div className="absolute inset-0" ref={ref}>
<div className="absolute top-48.25 left-15.75 h-129.25 w-99.75">
<div className={className} ref={ref}>
<PlaybackContext.Provider value={playback}>{children}</PlaybackContext.Provider>
</div>
);
}

function usePlayback(): TranscriptPlayback {
const playback = useContext(PlaybackContext);
if (!playback) {
throw new Error("PaywallBuild pieces must render inside PaywallBuildProvider");
}
return playback;
}

/**
* The Claude Code terminal rendered at natural size above the designer mock. Only shown
* below xl — scaling the terminal down with the rest of the mock made it unreadable, so
* small screens get it full width while desktop keeps it floating on the canvas.
*/
export function PaywallBuildTerminal() {
return (
<div className="xl:hidden">
<AgentTranscript playback={usePlayback()} />
</div>
);
}

/**
* Overlay for the designer mock: the paywall assembling itself on the canvas as each MCP
* call comes back. On xl the terminal floats over the canvas as in the original design and
* the phone keeps its artboard position. Below xl the terminal lives above the mock, the
* mock's side panels and tab bar are trimmed, and the phone (with its working-indicator
* effect) is bumped up 25% and centered on the now-free canvas — `top-35` leaves an even
* 48px of artboard padding under the toolbar and above the window's bottom edge.
*/
export function PaywallBuildCanvas() {
const playback = usePlayback();

return (
<div className="absolute inset-0">
<div className="absolute top-48.25 left-15.75 hidden h-129.25 w-99.75 xl:block">
<AgentTranscript playback={playback} />
</div>

<div
className="absolute top-37.75 left-140.75"
className="absolute top-35 left-1/2 origin-top -translate-x-1/2 scale-125 xl:top-37.75 xl:left-140.75 xl:translate-x-0 xl:scale-100"
style={{ height: PAYWALL_HEIGHT * PAYWALL_SCALE, width: PAYWALL_WIDTH * PAYWALL_SCALE }}
>
<div
Expand Down
16 changes: 11 additions & 5 deletions apps/www/src/features/www/landing/sections/crm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ export function LandingCrm() {
eyebrow="CRM"
title="Know the people behind your growth."
/>
<ScaledMock designWidth={1400}>
<div className="w-350 h-125 mt-6 shrink-0 relative">
<div className="left-97.5 top-0 w-252.5 flex flex-col items-start rounded-lg overflow-clip absolute">
<ScaledMock compactDesignWidth={1010} designWidth={1400}>
{/* Below xl the detail card stops floating over the table and stacks above it, so the
two panels become an ordinary flex column instead of the artboard's overlap. */}
<div className="w-full xl:w-350 xl:h-125 mt-6 shrink-0 relative flex flex-col gap-10 xl:block">
<div className="xl:left-97.5 xl:top-0 w-full xl:w-252.5 flex flex-col items-start rounded-lg overflow-clip relative xl:absolute">
<div className="self-stretch flex items-center py-3.5 px-6 gap-4 border-b border-b-solid [border-bottom-color:var(--color-zinc-900)]">
<div className="w-75 shrink-0 font-sans text-zinc-400 text-xs/4">
Person
Expand Down Expand Up @@ -205,9 +207,13 @@ export function LandingCrm() {
6 days ago
</div>
</div>
<div className="absolute top-px -bottom-2 inset-x-px" style={{ backgroundImage: 'radial-gradient(ellipse 67.23% 100% at 21.92% 0% in oklab, oklab(17.1% 0 0 / 0%) 17.44%, oklab(16.1% .0004 -0.001 / 33.8%) 45.31%, oklab(14.1% 0.001 -0.004) 100%)' }} />
{/* Blends the table under the floating detail card — its focal point sits where the
card overlaps, so it only makes sense while the two are stacked in z, not in flow. */}
<div className="hidden xl:block absolute top-px -bottom-2 inset-x-px" style={{ backgroundImage: 'radial-gradient(ellipse 67.23% 100% at 21.92% 0% in oklab, oklab(17.1% 0 0 / 0%) 17.44%, oklab(16.1% .0004 -0.001 / 33.8%) 45.31%, oklab(14.1% 0.001 -0.004) 100%)' }} />
{/* Mobile keeps just the bottom fade, so the row list still reads as continuing. */}
<div className="xl:hidden absolute top-px -bottom-2 inset-x-px" style={{ backgroundImage: 'linear-gradient(to bottom, oklab(17.1% 0 0 / 0%) 55%, oklab(14.1% 0.001 -0.004) 100%)' }} />
</div>
<div className="absolute left-0 -top-6 w-126 flex flex-col items-start rounded-2xl overflow-clip [box-shadow:#000000B8_0px_28px_90px] bg-origin-border border border-solid [border-color:var(--color-zinc-800)]" style={{ backgroundImage: 'radial-gradient(ellipse 87.36% 97.41499999999999% at 100% 0% in oklab, var(--color-zinc-900) 0%, var(--color-zinc-950) 100%)' }}>
<div className="relative xl:absolute -order-1 xl:order-none xl:left-0 xl:-top-6 w-full xl:w-126 flex flex-col items-start rounded-2xl overflow-clip [box-shadow:#000000B8_0px_28px_90px] bg-origin-border border border-solid [border-color:var(--color-zinc-800)]" style={{ backgroundImage: 'radial-gradient(ellipse 87.36% 97.41499999999999% at 100% 0% in oklab, var(--color-zinc-900) 0%, var(--color-zinc-950) 100%)' }}>
<div className="self-stretch flex items-center pt-6 pb-4 overflow-clip gap-4.5 px-7">
<div className="shrink-0 rounded-[999px] size-14" style={{ backgroundImage: 'linear-gradient(in oklab 135deg, oklab(68.1% -0.134 0.027) 0%, oklab(36.1% 0.011 -0.231) 100%)' }} />
<div className="flex flex-col grow basis-[0%] gap-1">
Expand Down
Loading
Loading