• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

agentic-dev-library / thumbcode / 21961208242

12 Feb 2026 07:29PM UTC coverage: 51.569% (-13.4%) from 64.947%
21961208242

push

github

web-flow
feat: complete Expo → Capacitor/Vite migration (#130)

## Summary

Full framework migration from Expo/React Native to Vite + Capacitor + React Router:

- **Framework**: Expo SDK 52 → Vite 6 + Capacitor 7
- **Routing**: expo-router → react-router-dom v7
- **Styling**: NativeWind → Tailwind CSS v4
- **Testing**: Jest → Vitest + @testing-library/react
- **Components**: React Native primitives → HTML/JSX (View→div, Text→span, etc.)
- **Build**: Metro bundler → Vite with HMR

### Key changes
- Removed all React Native and Expo dependencies
- Migrated 50+ components from RN to web HTML/JSX
- Rewrote 70 test files (844 tests passing)
- Replaced deploy-gh-pages.yml: Astro docs → Vite static site deployment
- Added android-release.yml: per-architecture debug APKs on GitHub releases
- Added ABI splits to build.gradle (armeabi-v7a, arm64-v8a, x86_64, universal)
- Fixed all biome lint, TypeScript, and E2E test issues
- Deleted dead `app/` directory (old Expo Router screens)

### CI Status
All critical checks passing: Lint & Type Check, Run Tests (844 passing), Build Web, Build Web + Capacitor Sync, E2E Tests (Web), Security Scan, CodeQL, Validate PR.

1329 of 2944 branches covered (45.14%)

Branch coverage included in aggregate %.

138 of 866 new or added lines in 82 files covered. (15.94%)

120 existing lines in 23 files now uncovered.

2188 of 3876 relevant lines covered (56.45%)

9.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

36.67
/src/components/error/ErrorFallback.tsx
1
/**
2
 * Error Fallback Component
3
 *
4
 * User-friendly error display with retry functionality.
5
 * Follows ThumbCode's organic design language.
6
 * Uses paint daube icons for brand consistency.
7
 */
8

9
import { ErrorIcon } from '@/components/icons';
10
import { Container, VStack } from '@/components/layout';
11
import { Text } from '@/components/ui';
12
import { organicBorderRadius } from '@/lib/organic-styles';
13

14
/** Props for the ErrorFallback component */
15
interface ErrorFallbackProps {
16
  /** The error that was caught, or null if unknown */
17
  error: Error | null;
18
  /** React component stack trace for debugging */
19
  componentStack?: string | null;
20
  /** Callback to retry the failed operation */
21
  onRetry?: () => void;
22
  /** Callback to report the issue (defaults to opening a GitHub issue) */
23
  onReportIssue?: () => void;
24
  /** Custom title for the error display */
25
  title?: string;
26
  /** Custom description message for the error display */
27
  message?: string;
28
}
29

30
export function ErrorFallback({
31
  error,
32
  componentStack,
33
  onRetry,
34
  onReportIssue,
35
  title = 'Something went wrong',
1✔
36
  message = "We're sorry, but something unexpected happened. Please try again.",
1✔
37
}: Readonly<ErrorFallbackProps>) {
38
  const isDev = import.meta.env.DEV;
1✔
39

40
  const handleReportIssue = () => {
1✔
41
    if (onReportIssue) {
×
42
      onReportIssue();
×
43
    } else {
44
      const issueTitle = encodeURIComponent(
×
45
        `[Bug] Error Report: ${error?.name || 'Unknown Error'}`
×
46
      );
47
      const issueBody = encodeURIComponent(
×
48
        `**Error Details**\nMessage: ${error?.message || 'No message'}\n\n**Component Stack**\n\`\`\`\n${componentStack || 'No stack trace'}\n\`\`\``
×
49
      );
50
      const url = `https://github.com/agentic-dev-library/thumbcode/issues/new?title=${issueTitle}&body=${issueBody}`;
×
51

NEW
52
      window.open(url, '_blank');
×
53
    }
54
  };
55

56
  return (
1✔
57
    <div className="flex-1 bg-charcoal">
58
      <Container padding="lg" className="flex-1 justify-center">
59
        <VStack spacing="lg" align="center">
60
          {/* Error Icon */}
61
          <div
62
            className="w-20 h-20 bg-coral-500/20 items-center justify-center"
63
            style={organicBorderRadius.hero}
64
          >
65
            <ErrorIcon size={40} color="coral" turbulence={0.25} />
66
          </div>
67

68
          {/* Error Title */}
69
          <Text
70
            size="xl"
71
            weight="bold"
72
            className="text-white text-center font-display"
73
            data-testid="error-title"
74
          >
75
            {title}
76
          </Text>
77

78
          {/* Error Message */}
79
          <Text className="text-neutral-400 text-center max-w-xs">{message}</Text>
80

81
          {/* Dev-only Error Details */}
82
          {isDev && error && (
3✔
83
            <div className="bg-surface p-4 w-full max-w-sm" style={organicBorderRadius.card}>
84
              <Text size="sm" weight="semibold" className="text-coral-500 mb-2">
85
                Debug Info
86
              </Text>
87
              <Text size="sm" className="text-neutral-400 font-mono mb-2">
88
                {error.name}: {error.message}
89
              </Text>
90
              {componentStack && (
2✔
91
                <Text size="xs" className="text-neutral-500 font-mono" numberOfLines={8}>
92
                  {componentStack}
93
                </Text>
94
              )}
95
            </div>
96
          )}
97

98
          {/* Retry Button */}
99
          {onRetry && (
1!
100
            <button
101
              type="button"
102
              onClick={onRetry}
103
              className="bg-coral-500 px-8 py-3 active:bg-coral-600"
104
              style={organicBorderRadius.cta}
105
            >
106
              <Text weight="semibold" className="text-white">
107
                Try Again
108
              </Text>
109
            </button>
110
          )}
111

112
          {/* Secondary Action */}
113
          <button
114
            type="button"
115
            className="py-2"
116
            onClick={handleReportIssue}
117
            data-testid="report-issue-button"
118
          >
119
            <Text size="sm" className="text-teal-500">
120
              Report Issue
121
            </Text>
122
          </button>
123
        </VStack>
124
      </Container>
125
    </div>
126
  );
127
}
128

129
/** Props for the CompactErrorFallback component, used for inline error displays */
130
interface CompactErrorFallbackProps {
131
  /** Error message to display */
132
  message?: string;
133
  /** Callback to retry the failed operation */
134
  onRetry?: () => void;
135
}
136

137
export function CompactErrorFallback({
138
  message = 'Failed to load',
×
139
  onRetry,
140
}: Readonly<CompactErrorFallbackProps>) {
141
  return (
×
142
    <div className="bg-surface/50 p-4" style={organicBorderRadius.card}>
143
      <VStack spacing="sm" align="center">
144
        <Text size="sm" className="text-neutral-400">
145
          {message}
146
        </Text>
147
        {onRetry && (
×
148
          <button type="button" onClick={onRetry}>
149
            <Text size="sm" className="text-teal-500">
150
              Tap to retry
151
            </Text>
152
          </button>
153
        )}
154
      </VStack>
155
    </div>
156
  );
157
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc