Add Foundry mobile layout with Tauri iOS/Android support

- Add responsive mobile layout with bottom pill tab bar, swipe navigation, and task list as home screen
- Add platform detection (useIsMobile hook) with viewport breakpoint and VITE_MOBILE build flag
- Mobile-optimize settings/billing/account pages (single-column layout with horizontal tabs)
- Add iOS safe area inset handling with 47px minimum padding
- Scaffold Tauri v2 mobile targets (iOS/Android) with platform-gated sidecar and capabilities
- Add notification sound support and mobile build script

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Nicholas Kissel 2026-03-12 22:35:54 -07:00
parent 436eb4a3a3
commit f464fa96ad
68 changed files with 8006 additions and 631 deletions

View file

@ -12,6 +12,7 @@ export interface MockFoundryUser {
name: string;
email: string;
githubLogin: string;
avatarUrl: string | null;
roleLabel: string;
eligibleOrganizationIds: string[];
}
@ -22,6 +23,8 @@ export interface MockFoundryOrganizationMember {
email: string;
role: "owner" | "admin" | "member";
state: "active" | "invited";
avatarUrl: string | null;
githubLogin: string | null;
}
export interface MockFoundryInvoice {
@ -162,6 +165,7 @@ function buildDefaultSnapshot(): MockFoundryAppSnapshot {
name: "Nathan",
email: "nathan@acme.dev",
githubLogin: "nathan",
avatarUrl: "https://github.com/NathanFlurry.png",
roleLabel: "Founder",
eligibleOrganizationIds: ["personal-nathan", "acme", "rivet"],
},
@ -170,6 +174,7 @@ function buildDefaultSnapshot(): MockFoundryAppSnapshot {
name: "Maya",
email: "maya@acme.dev",
githubLogin: "maya",
avatarUrl: "https://github.com/octocat.png",
roleLabel: "Staff Engineer",
eligibleOrganizationIds: ["acme"],
},
@ -178,6 +183,7 @@ function buildDefaultSnapshot(): MockFoundryAppSnapshot {
name: "Jamie",
email: "jamie@rivet.dev",
githubLogin: "jamie",
avatarUrl: "https://github.com/defunkt.png",
roleLabel: "Platform Lead",
eligibleOrganizationIds: ["personal-jamie", "rivet"],
},
@ -213,7 +219,17 @@ function buildDefaultSnapshot(): MockFoundryAppSnapshot {
paymentMethodLabel: "No card required",
invoices: [],
},
members: [{ id: "member-nathan", name: "Nathan", email: "nathan@acme.dev", role: "owner", state: "active" }],
members: [
{
id: "member-nathan",
name: "Nathan",
email: "nathan@acme.dev",
role: "owner",
state: "active",
avatarUrl: "https://github.com/NathanFlurry.png",
githubLogin: "NathanFlurry",
},
],
seatAssignments: ["nathan@acme.dev"],
repoCatalog: ["nathan/personal-site"],
},
@ -251,10 +267,34 @@ function buildDefaultSnapshot(): MockFoundryAppSnapshot {
],
},
members: [
{ id: "member-acme-nathan", name: "Nathan", email: "nathan@acme.dev", role: "owner", state: "active" },
{ id: "member-acme-maya", name: "Maya", email: "maya@acme.dev", role: "admin", state: "active" },
{ id: "member-acme-priya", name: "Priya", email: "priya@acme.dev", role: "member", state: "active" },
{ id: "member-acme-devon", name: "Devon", email: "devon@acme.dev", role: "member", state: "invited" },
{
id: "member-acme-nathan",
name: "Nathan",
email: "nathan@acme.dev",
role: "owner",
state: "active",
avatarUrl: "https://github.com/NathanFlurry.png",
githubLogin: "NathanFlurry",
},
{
id: "member-acme-maya",
name: "Maya",
email: "maya@acme.dev",
role: "admin",
state: "active",
avatarUrl: "https://github.com/octocat.png",
githubLogin: "octocat",
},
{
id: "member-acme-priya",
name: "Priya",
email: "priya@acme.dev",
role: "member",
state: "active",
avatarUrl: "https://github.com/mona.png",
githubLogin: "mona",
},
{ id: "member-acme-devon", name: "Devon", email: "devon@acme.dev", role: "member", state: "invited", avatarUrl: null, githubLogin: null },
],
seatAssignments: ["nathan@acme.dev", "maya@acme.dev"],
repoCatalog: ["acme/backend", "acme/frontend", "acme/infra"],
@ -290,9 +330,33 @@ function buildDefaultSnapshot(): MockFoundryAppSnapshot {
invoices: [{ id: "inv-rivet-001", label: "Team pilot", issuedAt: "2026-03-04", amountUsd: 0, status: "paid" }],
},
members: [
{ id: "member-rivet-jamie", name: "Jamie", email: "jamie@rivet.dev", role: "owner", state: "active" },
{ id: "member-rivet-nathan", name: "Nathan", email: "nathan@acme.dev", role: "member", state: "active" },
{ id: "member-rivet-lena", name: "Lena", email: "lena@rivet.dev", role: "admin", state: "active" },
{
id: "member-rivet-jamie",
name: "Jamie",
email: "jamie@rivet.dev",
role: "owner",
state: "active",
avatarUrl: "https://github.com/defunkt.png",
githubLogin: "defunkt",
},
{
id: "member-rivet-nathan",
name: "Nathan",
email: "nathan@acme.dev",
role: "member",
state: "active",
avatarUrl: "https://github.com/NathanFlurry.png",
githubLogin: "NathanFlurry",
},
{
id: "member-rivet-lena",
name: "Lena",
email: "lena@rivet.dev",
role: "admin",
state: "active",
avatarUrl: "https://github.com/mojombo.png",
githubLogin: "mojombo",
},
],
seatAssignments: ["jamie@rivet.dev"],
repoCatalog: ["rivet/dashboard", "rivet/agents", "rivet/billing", "rivet/infrastructure"],
@ -327,7 +391,17 @@ function buildDefaultSnapshot(): MockFoundryAppSnapshot {
paymentMethodLabel: "No card required",
invoices: [],
},
members: [{ id: "member-jamie", name: "Jamie", email: "jamie@rivet.dev", role: "owner", state: "active" }],
members: [
{
id: "member-jamie",
name: "Jamie",
email: "jamie@rivet.dev",
role: "owner",
state: "active",
avatarUrl: "https://github.com/defunkt.png",
githubLogin: "defunkt",
},
],
seatAssignments: ["jamie@rivet.dev"],
repoCatalog: ["jamie/demo-app"],
},

View file

@ -100,6 +100,7 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
diffs: {},
fileTree: [],
minutesUsed: 0,
presence: [],
};
this.updateState((current) => ({

View file

@ -435,6 +435,10 @@ export function buildInitialTasks(): Task[] {
},
],
minutesUsed: 42,
presence: [
{ memberId: "member-acme-nathan", name: "Nathan", avatarUrl: "https://github.com/NathanFlurry.png", lastSeenAtMs: minutesAgo(1) },
{ memberId: "member-acme-maya", name: "Maya", avatarUrl: "https://github.com/octocat.png", lastSeenAtMs: minutesAgo(0), typing: true },
],
},
{
id: "h2",
@ -535,6 +539,7 @@ export function buildInitialTasks(): Task[] {
},
],
minutesUsed: 187,
presence: [{ memberId: "member-acme-priya", name: "Priya", avatarUrl: "https://github.com/mona.png", lastSeenAtMs: minutesAgo(0) }],
},
{
id: "h3",
@ -609,6 +614,7 @@ export function buildInitialTasks(): Task[] {
},
],
minutesUsed: 23,
presence: [],
},
// ── rivet-dev/rivet ──
{
@ -744,6 +750,11 @@ export function buildInitialTasks(): Task[] {
},
],
minutesUsed: 5,
presence: [
{ memberId: "member-acme-nathan", name: "Nathan", avatarUrl: "https://github.com/NathanFlurry.png", lastSeenAtMs: minutesAgo(0) },
{ memberId: "member-acme-maya", name: "Maya", avatarUrl: "https://github.com/octocat.png", lastSeenAtMs: minutesAgo(2) },
{ memberId: "member-acme-priya", name: "Priya", avatarUrl: "https://github.com/mona.png", lastSeenAtMs: minutesAgo(5) },
],
},
{
id: "h5",
@ -800,6 +811,7 @@ export function buildInitialTasks(): Task[] {
diffs: {},
fileTree: [],
minutesUsed: 312,
presence: [{ memberId: "member-acme-maya", name: "Maya", avatarUrl: "https://github.com/octocat.png", lastSeenAtMs: minutesAgo(45) }],
},
// ── rivet-dev/cloud ──
{
@ -909,6 +921,7 @@ export function buildInitialTasks(): Task[] {
},
],
minutesUsed: 0,
presence: [],
},
// ── rivet-dev/engine-ee ──
{
@ -1023,6 +1036,7 @@ export function buildInitialTasks(): Task[] {
},
],
minutesUsed: 78,
presence: [],
},
// ── rivet-dev/engine-ee (archived) ──
{
@ -1065,6 +1079,7 @@ export function buildInitialTasks(): Task[] {
diffs: {},
fileTree: [],
minutesUsed: 15,
presence: [],
},
// ── rivet-dev/secure-exec ──
{
@ -1118,6 +1133,7 @@ export function buildInitialTasks(): Task[] {
diffs: {},
fileTree: [],
minutesUsed: 3,
presence: [],
},
];
}