@mr.dj2u/library-registry 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -0
  3. package/THIRD_PARTY_NOTICES.md +90 -0
  4. package/assets/create-expo-app/sdk-56/assets/images/expo-badge-white.png +0 -0
  5. package/assets/create-expo-app/sdk-56/assets/images/expo-badge.png +0 -0
  6. package/assets/create-expo-app/sdk-56/assets/images/expo-logo.png +0 -0
  7. package/assets/create-expo-app/sdk-56/assets/images/logo-glow.png +0 -0
  8. package/assets/create-expo-app/sdk-56/assets/images/react-logo.png +0 -0
  9. package/assets/create-expo-app/sdk-56/assets/images/react-logo@2x.png +0 -0
  10. package/assets/create-expo-app/sdk-56/assets/images/react-logo@3x.png +0 -0
  11. package/assets/create-expo-app/sdk-56/assets/images/tabIcons/explore.png +0 -0
  12. package/assets/create-expo-app/sdk-56/assets/images/tabIcons/explore@2x.png +0 -0
  13. package/assets/create-expo-app/sdk-56/assets/images/tabIcons/explore@3x.png +0 -0
  14. package/assets/create-expo-app/sdk-56/assets/images/tabIcons/home.png +0 -0
  15. package/assets/create-expo-app/sdk-56/assets/images/tabIcons/home@2x.png +0 -0
  16. package/assets/create-expo-app/sdk-56/assets/images/tabIcons/home@3x.png +0 -0
  17. package/assets/create-expo-app/sdk-56/assets/images/tutorial-web.png +0 -0
  18. package/assets/create-expo-app/sdk-56/src/app/_layout.tsx +15 -0
  19. package/assets/create-expo-app/sdk-56/src/app/explore.tsx +180 -0
  20. package/assets/create-expo-app/sdk-56/src/app/index.tsx +98 -0
  21. package/assets/create-expo-app/sdk-56/src/components/animated-icon.module.css +6 -0
  22. package/assets/create-expo-app/sdk-56/src/components/animated-icon.tsx +132 -0
  23. package/assets/create-expo-app/sdk-56/src/components/animated-icon.web.tsx +108 -0
  24. package/assets/create-expo-app/sdk-56/src/components/app-tabs.tsx +32 -0
  25. package/assets/create-expo-app/sdk-56/src/components/app-tabs.web.tsx +115 -0
  26. package/assets/create-expo-app/sdk-56/src/components/external-link.tsx +25 -0
  27. package/assets/create-expo-app/sdk-56/src/components/hint-row.tsx +35 -0
  28. package/assets/create-expo-app/sdk-56/src/components/themed-text.tsx +73 -0
  29. package/assets/create-expo-app/sdk-56/src/components/themed-view.tsx +16 -0
  30. package/assets/create-expo-app/sdk-56/src/components/ui/collapsible.tsx +65 -0
  31. package/assets/create-expo-app/sdk-56/src/components/web-badge.tsx +43 -0
  32. package/assets/create-expo-app/sdk-56/src/constants/theme.ts +65 -0
  33. package/assets/create-expo-app/sdk-56/src/global.css +9 -0
  34. package/assets/create-expo-app/sdk-56/src/hooks/use-color-scheme.ts +1 -0
  35. package/assets/create-expo-app/sdk-56/src/hooks/use-color-scheme.web.ts +21 -0
  36. package/assets/create-expo-app/sdk-56/src/hooks/use-theme.ts +14 -0
  37. package/assets/create-expo-stack/2.21.3/base/components/BackButton.tsx +23 -0
  38. package/assets/create-expo-stack/2.21.3/base/components/Button.tsx +42 -0
  39. package/assets/create-expo-stack/2.21.3/base/components/Container.tsx +14 -0
  40. package/assets/create-expo-stack/2.21.3/base/components/EditScreenInfo.tsx +36 -0
  41. package/assets/create-expo-stack/2.21.3/base/components/HeaderButton.tsx +31 -0
  42. package/assets/create-expo-stack/2.21.3/base/components/ScreenContent.tsx +40 -0
  43. package/assets/create-expo-stack/2.21.3/base/components/TabBarIcon.tsx +15 -0
  44. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/ActivityIndicator.tsx +10 -0
  45. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Avatar.tsx +32 -0
  46. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Button.tsx +141 -0
  47. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/DatePicker/DatePicker.android.tsx +77 -0
  48. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/DatePicker/DatePicker.tsx +23 -0
  49. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/DatePicker/index.ts +1 -0
  50. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Icon/Icon.ios.tsx +55 -0
  51. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Icon/Icon.tsx +58 -0
  52. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Icon/index.ts +16 -0
  53. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Icon/types.ts +19 -0
  54. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Picker.tsx +42 -0
  55. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/ProgressIndicator.tsx +68 -0
  56. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Slider.tsx +28 -0
  57. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Text.tsx +55 -0
  58. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/ThemeToggle.tsx +33 -0
  59. package/assets/create-expo-stack/2.21.3-mrdj.2/src/components/nativewindui/Toggle.tsx +20 -0
  60. package/assets/create-expo-stack/2.21.3-mrdj.2/src/lib/cn.ts +6 -0
  61. package/assets/create-expo-stack/2.21.3-mrdj.2/src/lib/useColorScheme.tsx +21 -0
  62. package/assets/create-expo-stack/2.21.3-mrdj.2/src/lib/useHeaderSearchBar.tsx +33 -0
  63. package/assets/create-expo-stack/2.21.3-mrdj.2/src/theme/colors.ts +123 -0
  64. package/assets/create-expo-stack/2.21.3-mrdj.2/src/theme/index.ts +32 -0
  65. package/assets/create-expo-stack/2.21.3-mrdj.2/src/theme/with-opacity.ts +155 -0
  66. package/assets/mds/routes/drawer-tabs/data.tsx +1 -0
  67. package/assets/mds/routes/drawer-tabs/index.tsx +1 -0
  68. package/assets/mds/routes/drawer-tabs/nativewindui.tsx +1 -0
  69. package/assets/mds/routes/drawer-tabs/sdk-56.tsx +1 -0
  70. package/assets/mds/routes/drawer-tabs/stylist.tsx +1 -0
  71. package/assets/mds/scripts/stylist-sync-android.mjs +39 -0
  72. package/assets/mds/src/app/exposition/data.tsx +1 -0
  73. package/assets/mds/src/app/exposition/index.tsx +1 -0
  74. package/assets/mds/src/app/exposition/nativewindui.tsx +1 -0
  75. package/assets/mds/src/app/exposition/sdk-56.tsx +1 -0
  76. package/assets/mds/src/app/exposition/stylist-sync+api.ts +241 -0
  77. package/assets/mds/src/app/exposition/stylist.tsx +1 -0
  78. package/assets/mds/src/app/onboarding/account-setup.tsx +1 -0
  79. package/assets/mds/src/app/onboarding/agreement.tsx +1 -0
  80. package/assets/mds/src/app/onboarding/terms.tsx +1 -0
  81. package/assets/mds/src/app/onboarding.tsx +1 -0
  82. package/assets/mds/src/app/settings.tsx +1 -0
  83. package/assets/mds/src/components/exposition/index.ts +8 -0
  84. package/assets/mds/src/components/exposition/notice.tsx +3 -0
  85. package/assets/mds/src/components/exposition/package-card.tsx +76 -0
  86. package/assets/mds/src/components/swmansion/animated-pressable.tsx +54 -0
  87. package/assets/mds/src/components/swmansion/gesture-card.tsx +54 -0
  88. package/assets/mds/src/components/swmansion/keyboard-form.tsx +73 -0
  89. package/assets/mds/src/components/swmansion/screens-card.tsx +40 -0
  90. package/assets/mds/src/components/swmansion/software-mansion-logo.tsx +15 -0
  91. package/assets/mds/src/components/swmansion/svg-mark.tsx +16 -0
  92. package/assets/mds/src/data/mock-app.ts +21 -0
  93. package/assets/mds/src/features/exposition/data-screen.tsx +152 -0
  94. package/assets/mds/src/features/exposition/embedded-fonts.ts +72 -0
  95. package/assets/mds/src/features/exposition/expo-sdk-56-screen.tsx +709 -0
  96. package/assets/mds/src/features/exposition/exposition-screen.tsx +191 -0
  97. package/assets/mds/src/features/exposition/nativewindui-screen.tsx +125 -0
  98. package/assets/mds/src/features/exposition/stylist-screen.tsx +3456 -0
  99. package/assets/mds/src/features/onboarding/account-setup-screen.tsx +54 -0
  100. package/assets/mds/src/features/onboarding/agreement-screen.tsx +6 -0
  101. package/assets/mds/src/features/onboarding/components/legal-document-view.tsx +103 -0
  102. package/assets/mds/src/features/onboarding/legal-documents.ts +80 -0
  103. package/assets/mds/src/features/onboarding/onboarding-screen.tsx +167 -0
  104. package/assets/mds/src/features/onboarding/terms-screen.tsx +6 -0
  105. package/assets/mds/src/features/settings/settings-screen.tsx +57 -0
  106. package/assets/mds/src/services/local-data.native.ts +103 -0
  107. package/assets/mds/src/services/local-data.ts +38 -0
  108. package/assets/mds/src/theme/font-assets.ts +3 -0
  109. package/assets/mds/src/theme/provider.tsx +59 -0
  110. package/assets/mds/src/theme/tokens.ts +180 -0
  111. package/dist/catalog.d.ts +3 -0
  112. package/dist/catalog.d.ts.map +1 -0
  113. package/dist/catalog.js +1110 -0
  114. package/dist/catalog.js.map +1 -0
  115. package/dist/index.d.ts +5 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +4 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/registry.d.ts +7 -0
  120. package/dist/registry.d.ts.map +1 -0
  121. package/dist/registry.js +479 -0
  122. package/dist/registry.js.map +1 -0
  123. package/dist/types.d.ts +169 -0
  124. package/dist/types.d.ts.map +1 -0
  125. package/dist/types.js +2 -0
  126. package/dist/types.js.map +1 -0
  127. package/dist/validation.d.ts +6 -0
  128. package/dist/validation.d.ts.map +1 -0
  129. package/dist/validation.js +209 -0
  130. package/dist/validation.js.map +1 -0
  131. package/package.json +51 -0
@@ -0,0 +1,241 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { access, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ import defaultThemeTokens from '@/theme/tokens';
6
+
7
+ interface SyncResponse {
8
+ projectPath: string;
9
+ updatedFiles: string[];
10
+ }
11
+
12
+ interface StylistSyncRequestBody {
13
+ theme: unknown;
14
+ metadata?: {
15
+ writePolicy?: 'managed' | 'overwrite';
16
+ styleLibrary?:
17
+ | 'auto'
18
+ | 'uniwind'
19
+ | 'nativewind'
20
+ | 'nativewindui'
21
+ | 'unistyles'
22
+ | 'restyle'
23
+ | 'tamagui'
24
+ | 'stylesheet';
25
+ };
26
+ }
27
+
28
+ function parseSyncResponse(stdout: string): SyncResponse {
29
+ const trimmed = stdout.trim();
30
+ if (!trimmed) {
31
+ throw new Error('Stylist sync returned empty output.');
32
+ }
33
+ try {
34
+ return JSON.parse(trimmed) as SyncResponse;
35
+ } catch {
36
+ const match = trimmed.match(/\{[\s\S]*\}$/);
37
+ if (!match) {
38
+ throw new Error('Stylist sync returned non-JSON output.');
39
+ }
40
+ return JSON.parse(match[0]) as SyncResponse;
41
+ }
42
+ }
43
+
44
+ export async function POST(request: Request) {
45
+ try {
46
+ const payload = (await request.json()) as unknown;
47
+ const normalized = normalizeSyncPayload(payload);
48
+ const result = await runStylistSync(JSON.stringify(normalized.theme), normalized.metadata);
49
+ return Response.json(result);
50
+ } catch (error) {
51
+ return Response.json(
52
+ { error: error instanceof Error ? error.message : 'Unknown stylist sync error' },
53
+ { status: 400 }
54
+ );
55
+ }
56
+ }
57
+
58
+ export async function GET() {
59
+ const configPath = path.resolve(process.cwd(), 'project', 'stylist.config.json');
60
+ const themePath = path.resolve(process.cwd(), 'project', 'theme.json');
61
+ const stylePath = path.resolve(process.cwd(), 'project', 'style.md');
62
+
63
+ const themeFromJson = await readThemeJson(themePath);
64
+ const themeFromStyle = await readThemeFromStyleMarkdown(stylePath);
65
+ const resolvedTheme = themeFromStyle ?? themeFromJson ?? defaultThemeTokens;
66
+ const themeSource = themeFromStyle ? 'style.md' : themeFromJson ? 'theme.json' : 'default';
67
+ const mismatchDetected =
68
+ Boolean(themeFromJson) &&
69
+ Boolean(themeFromStyle) &&
70
+ JSON.stringify(themeFromJson) !== JSON.stringify(themeFromStyle);
71
+ try {
72
+ const raw = await readFile(configPath, 'utf8');
73
+ const parsed = JSON.parse(raw) as { writePolicy?: string; styleLibrary?: string };
74
+ return Response.json({
75
+ hasConfig: true,
76
+ writePolicy: parsed.writePolicy ?? null,
77
+ styleLibrary: parsed.styleLibrary ?? null,
78
+ theme: resolvedTheme,
79
+ themeSource,
80
+ mismatchDetected,
81
+ });
82
+ } catch {
83
+ return Response.json({
84
+ hasConfig: false,
85
+ writePolicy: null,
86
+ styleLibrary: null,
87
+ theme: resolvedTheme,
88
+ themeSource,
89
+ mismatchDetected,
90
+ });
91
+ }
92
+ }
93
+
94
+ function normalizeSyncPayload(value: unknown): StylistSyncRequestBody {
95
+ if (!value || typeof value !== 'object') {
96
+ throw new Error('Invalid stylist payload.');
97
+ }
98
+
99
+ const asRecord = value as Record<string, unknown>;
100
+ if ('theme' in asRecord && asRecord.theme) {
101
+ return {
102
+ theme: asRecord.theme,
103
+ metadata:
104
+ asRecord.metadata && typeof asRecord.metadata === 'object'
105
+ ? (asRecord.metadata as StylistSyncRequestBody['metadata'])
106
+ : undefined,
107
+ };
108
+ }
109
+
110
+ if ('metadata' in asRecord) {
111
+ throw new Error('Invalid stylist payload: missing theme.');
112
+ }
113
+
114
+ return { theme: value };
115
+ }
116
+
117
+ async function runStylistSync(
118
+ inputJson: string,
119
+ metadata?: StylistSyncRequestBody['metadata']
120
+ ): Promise<SyncResponse> {
121
+ const tempDir = path.resolve(process.cwd(), '.expo', 'stylist-sync');
122
+ await mkdir(tempDir, { recursive: true });
123
+ const tempInputPath = path.join(
124
+ tempDir,
125
+ `theme-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
126
+ );
127
+ await writeFile(tempInputPath, inputJson, 'utf8');
128
+
129
+ const fileExists = async (filePath: string): Promise<boolean> => {
130
+ try {
131
+ await access(filePath);
132
+ return true;
133
+ } catch {
134
+ return false;
135
+ }
136
+ };
137
+
138
+ const runAttempt = async (
139
+ command: string,
140
+ args: string[],
141
+ env: NodeJS.ProcessEnv
142
+ ): Promise<SyncResponse> => {
143
+ return await new Promise<SyncResponse>((resolve, reject) => {
144
+ const child = spawn(command, args, {
145
+ cwd: process.cwd(),
146
+ stdio: ['ignore', 'pipe', 'pipe'],
147
+ windowsHide: true,
148
+ env,
149
+ });
150
+
151
+ let stdout = '';
152
+ let stderr = '';
153
+ child.stdout.on('data', (chunk) => {
154
+ stdout += String(chunk);
155
+ });
156
+ child.stderr.on('data', (chunk) => {
157
+ stderr += String(chunk);
158
+ });
159
+
160
+ child.on('error', (error) => {
161
+ reject(error);
162
+ });
163
+
164
+ const timeout = setTimeout(() => {
165
+ child.kill();
166
+ reject(new Error('Stylist sync timed out after 120 seconds.'));
167
+ }, 120000);
168
+
169
+ child.on('close', (code) => {
170
+ clearTimeout(timeout);
171
+ if (code !== 0) {
172
+ reject(
173
+ new Error(stderr.trim() || `Stylist sync failed with exit code ${code ?? 'unknown'}.`)
174
+ );
175
+ return;
176
+ }
177
+
178
+ try {
179
+ resolve(parseSyncResponse(stdout));
180
+ } catch (error) {
181
+ reject(
182
+ new Error(
183
+ `Failed to parse stylist sync output: ${error instanceof Error ? error.message : String(error)}${stderr.trim() ? ` | stderr: ${stderr.trim()}` : ''}`
184
+ )
185
+ );
186
+ }
187
+ });
188
+ });
189
+ };
190
+
191
+ const scriptPath = path.resolve(process.cwd(), 'scripts', 'stylist-sync-android.mjs');
192
+ const env = {
193
+ ...process.env,
194
+ MDS_STYLIST_INPUT_FILE: path.relative(process.cwd(), tempInputPath),
195
+ MDS_STYLIST_WRITE_POLICY: metadata?.writePolicy ?? 'managed',
196
+ MDS_STYLIST_STYLE_LIBRARY: metadata?.styleLibrary ?? 'auto',
197
+ };
198
+
199
+ try {
200
+ if (!(await fileExists(scriptPath))) {
201
+ throw new Error('Stylist sync helper is missing. Run npm install, then retry.');
202
+ }
203
+ return await runAttempt(process.execPath, [scriptPath], env);
204
+ } finally {
205
+ try {
206
+ await unlink(tempInputPath);
207
+ } catch {
208
+ // no-op
209
+ }
210
+ }
211
+ }
212
+
213
+ async function readThemeJson(filePath: string): Promise<unknown | null> {
214
+ try {
215
+ const raw = await readFile(filePath, 'utf8');
216
+ return JSON.parse(raw) as unknown;
217
+ } catch {
218
+ return null;
219
+ }
220
+ }
221
+
222
+ async function readThemeFromStyleMarkdown(filePath: string): Promise<unknown | null> {
223
+ try {
224
+ const raw = await readFile(filePath, 'utf8');
225
+ const startToken = '<!-- MDS_STYLIST_THEME_START -->';
226
+ const endToken = '<!-- MDS_STYLIST_THEME_END -->';
227
+ const startIndex = raw.indexOf(startToken);
228
+ const endIndex = raw.indexOf(endToken);
229
+ if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
230
+ return null;
231
+ }
232
+ const block = raw.slice(startIndex, endIndex + endToken.length);
233
+ const match = block.match(/```json\s*([\s\S]*?)\s*```/i);
234
+ if (!match?.[1]) {
235
+ return null;
236
+ }
237
+ return JSON.parse(match[1]) as unknown;
238
+ } catch {
239
+ return null;
240
+ }
241
+ }
@@ -0,0 +1 @@
1
+ export { default } from '@/features/exposition/stylist-screen';
@@ -0,0 +1 @@
1
+ export { default } from '@/features/onboarding/account-setup-screen';
@@ -0,0 +1 @@
1
+ export { default } from '@/features/onboarding/agreement-screen';
@@ -0,0 +1 @@
1
+ export { default } from '@/features/onboarding/terms-screen';
@@ -0,0 +1 @@
1
+ export { default } from '@/features/onboarding/onboarding-screen';
@@ -0,0 +1 @@
1
+ export { default } from '@/features/settings/settings-screen';
@@ -0,0 +1,8 @@
1
+ export { ExpositionNotice } from './notice';
2
+ export { PackageCard } from './package-card';
3
+ export { AnimatedPressable } from '../swmansion/animated-pressable';
4
+ export { GestureCard } from '../swmansion/gesture-card';
5
+ export { KeyboardForm } from '../swmansion/keyboard-form';
6
+ export { ScreensCard } from '../swmansion/screens-card';
7
+ export { SoftwareMansionLogo } from '../swmansion/software-mansion-logo';
8
+ export { SvgMark } from '../swmansion/svg-mark';
@@ -0,0 +1,3 @@
1
+ export function ExpositionNotice() {
2
+ return null;
3
+ }
@@ -0,0 +1,76 @@
1
+ import type { ReactNode } from 'react';
2
+ import { StyleSheet, Text, View } from 'react-native';
3
+
4
+ import { useAppTheme } from '../../theme/provider';
5
+
6
+ interface PackageCardProps {
7
+ title: string;
8
+ packageName: string;
9
+ body: string;
10
+ children?: ReactNode;
11
+ }
12
+
13
+ export function PackageCard({ title, packageName, body, children }: PackageCardProps) {
14
+ const theme = useAppTheme();
15
+ const colors = theme.activeColors;
16
+
17
+ return (
18
+ <View
19
+ style={[
20
+ styles.card,
21
+ {
22
+ backgroundColor: colors.surface,
23
+ borderColor: colors.primary,
24
+ borderRadius: theme.layout.radius,
25
+ },
26
+ ]}>
27
+ <Text style={[styles.packageName, { color: colors.text }]}>{packageName}</Text>
28
+ <Text
29
+ style={[
30
+ styles.title,
31
+ {
32
+ color: colors.text,
33
+ fontFamily: theme.typography.fontFamily,
34
+ fontWeight:
35
+ theme.typography.fontFamily === 'System' ||
36
+ theme.typography.fontFamily === 'monospace'
37
+ ? '800'
38
+ : 'normal',
39
+ },
40
+ ]}>
41
+ {title}
42
+ </Text>
43
+ <Text style={[styles.body, { color: colors.text }]}>{body}</Text>
44
+ {children ? <View style={styles.demo}>{children}</View> : null}
45
+ </View>
46
+ );
47
+ }
48
+
49
+ const styles = StyleSheet.create({
50
+ card: {
51
+ backgroundColor: '#ffffff',
52
+ borderColor: '#e5e7eb',
53
+ borderRadius: 12,
54
+ borderWidth: 1,
55
+ gap: 8,
56
+ padding: 16,
57
+ },
58
+ packageName: {
59
+ color: '#6b7280',
60
+ fontSize: 12,
61
+ fontWeight: '700',
62
+ },
63
+ title: {
64
+ color: '#111827',
65
+ fontSize: 17,
66
+ fontWeight: '800',
67
+ },
68
+ body: {
69
+ color: '#4b5563',
70
+ fontSize: 14,
71
+ lineHeight: 20,
72
+ },
73
+ demo: {
74
+ marginTop: 6,
75
+ },
76
+ });
@@ -0,0 +1,54 @@
1
+ import type { ReactNode } from 'react';
2
+ import { Pressable, StyleSheet, Text } from 'react-native';
3
+ import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
4
+
5
+ const AnimatedPressableBase = Animated.createAnimatedComponent(Pressable);
6
+
7
+ interface AnimatedPressableProps {
8
+ backgroundColor?: string;
9
+ children?: ReactNode;
10
+ label?: string;
11
+ onPress?: () => void;
12
+ textColor?: string;
13
+ }
14
+
15
+ export function AnimatedPressable({
16
+ backgroundColor = '#111827',
17
+ children,
18
+ label = 'Reanimated press demo',
19
+ onPress,
20
+ textColor = '#ffffff',
21
+ }: AnimatedPressableProps) {
22
+ const pressed = useSharedValue(0);
23
+ const animatedStyle = useAnimatedStyle(() => ({
24
+ transform: [{ scale: withTiming(pressed.value ? 0.97 : 1, { duration: 120 }) }],
25
+ }));
26
+
27
+ return (
28
+ <AnimatedPressableBase
29
+ onPress={onPress}
30
+ onPressIn={() => {
31
+ pressed.value = 1;
32
+ }}
33
+ onPressOut={() => {
34
+ pressed.value = 0;
35
+ }}
36
+ style={[styles.button, { backgroundColor }, animatedStyle]}>
37
+ {children ?? <Text style={[styles.label, { color: textColor }]}>{label}</Text>}
38
+ </AnimatedPressableBase>
39
+ );
40
+ }
41
+
42
+ const styles = StyleSheet.create({
43
+ button: {
44
+ backgroundColor: '#111827',
45
+ borderRadius: 10,
46
+ paddingHorizontal: 16,
47
+ paddingVertical: 12,
48
+ },
49
+ label: {
50
+ fontSize: 15,
51
+ fontWeight: '700',
52
+ textAlign: 'center',
53
+ },
54
+ });
@@ -0,0 +1,54 @@
1
+ import { StyleSheet, Text } from 'react-native';
2
+ import { Gesture, GestureDetector } from 'react-native-gesture-handler';
3
+ import Animated, { useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
4
+
5
+ interface GestureCardProps {
6
+ title: string;
7
+ body: string;
8
+ }
9
+
10
+ export function GestureCard({ title, body }: GestureCardProps) {
11
+ const offset = useSharedValue(0);
12
+ const pan = Gesture.Pan()
13
+ .onChange((event) => {
14
+ offset.value = event.translationX;
15
+ })
16
+ .onFinalize(() => {
17
+ offset.value = withSpring(0);
18
+ });
19
+
20
+ const style = useAnimatedStyle(() => ({
21
+ transform: [{ translateX: offset.value }],
22
+ }));
23
+
24
+ return (
25
+ <GestureDetector gesture={pan}>
26
+ <Animated.View style={[styles.card, style]}>
27
+ <Text style={styles.title}>{title}</Text>
28
+ <Text style={styles.body}>{body}</Text>
29
+ </Animated.View>
30
+ </GestureDetector>
31
+ );
32
+ }
33
+
34
+ const styles = StyleSheet.create({
35
+ card: {
36
+ backgroundColor: '#ffffff',
37
+ borderColor: '#e5e7eb',
38
+ borderRadius: 12,
39
+ borderWidth: 1,
40
+ padding: 16,
41
+ boxShadow: '0 6px 10px rgba(0, 0, 0, 0.08)',
42
+ },
43
+ title: {
44
+ color: '#111827',
45
+ fontSize: 16,
46
+ fontWeight: '700',
47
+ },
48
+ body: {
49
+ color: '#4b5563',
50
+ fontSize: 14,
51
+ lineHeight: 20,
52
+ marginTop: 8,
53
+ },
54
+ });
@@ -0,0 +1,73 @@
1
+ import { Keyboard, Platform, ScrollView, StyleSheet, TextInput } from 'react-native';
2
+ import { KeyboardAwareScrollView, KeyboardToolbar } from 'react-native-keyboard-controller';
3
+
4
+ export function KeyboardForm() {
5
+ if (Platform.OS === 'web') {
6
+ return (
7
+ <ScrollView contentContainerStyle={styles.form} style={styles.scroller}>
8
+ <TextInput
9
+ blurOnSubmit
10
+ onSubmitEditing={Keyboard.dismiss}
11
+ placeholder="Project note"
12
+ returnKeyType="done"
13
+ style={styles.input}
14
+ />
15
+ <TextInput
16
+ blurOnSubmit
17
+ multiline
18
+ onSubmitEditing={Keyboard.dismiss}
19
+ placeholder="Details"
20
+ returnKeyType="done"
21
+ style={[styles.input, styles.multiline]}
22
+ />
23
+ </ScrollView>
24
+ );
25
+ }
26
+ return (
27
+ <>
28
+ <KeyboardAwareScrollView
29
+ bottomOffset={72}
30
+ contentContainerStyle={styles.form}
31
+ style={styles.scroller}>
32
+ <TextInput
33
+ blurOnSubmit
34
+ onSubmitEditing={Keyboard.dismiss}
35
+ placeholder="Project note"
36
+ returnKeyType="done"
37
+ style={styles.input}
38
+ />
39
+ <TextInput
40
+ blurOnSubmit
41
+ multiline
42
+ onSubmitEditing={Keyboard.dismiss}
43
+ placeholder="Details"
44
+ returnKeyType="done"
45
+ style={[styles.input, styles.multiline]}
46
+ />
47
+ </KeyboardAwareScrollView>
48
+ <KeyboardToolbar onDoneCallback={Keyboard.dismiss} />
49
+ </>
50
+ );
51
+ }
52
+
53
+ const styles = StyleSheet.create({
54
+ scroller: {
55
+ maxHeight: 220,
56
+ },
57
+ form: {
58
+ gap: 12,
59
+ paddingVertical: 8,
60
+ },
61
+ input: {
62
+ borderColor: '#d1d5db',
63
+ borderRadius: 10,
64
+ borderWidth: 1,
65
+ minHeight: 44,
66
+ paddingHorizontal: 12,
67
+ },
68
+ multiline: {
69
+ minHeight: 88,
70
+ paddingTop: 10,
71
+ textAlignVertical: 'top',
72
+ },
73
+ });
@@ -0,0 +1,40 @@
1
+ import { useEffect } from 'react';
2
+ import { StyleSheet, Text, View } from 'react-native';
3
+ import { enableScreens } from 'react-native-screens';
4
+
5
+ export function ScreensCard() {
6
+ useEffect(() => {
7
+ enableScreens(true);
8
+ }, []);
9
+
10
+ return (
11
+ <View style={styles.card}>
12
+ <Text style={styles.title}>Native Screens</Text>
13
+ <Text style={styles.body}>
14
+ react-native-screens is enabled so navigation can use native screen primitives for better
15
+ memory and lifecycle behavior.
16
+ </Text>
17
+ </View>
18
+ );
19
+ }
20
+
21
+ const styles = StyleSheet.create({
22
+ card: {
23
+ backgroundColor: '#eef2ff',
24
+ borderColor: '#c7d2fe',
25
+ borderRadius: 12,
26
+ borderWidth: 1,
27
+ padding: 16,
28
+ },
29
+ title: {
30
+ color: '#312e81',
31
+ fontSize: 16,
32
+ fontWeight: '700',
33
+ },
34
+ body: {
35
+ color: '#4338ca',
36
+ fontSize: 14,
37
+ lineHeight: 20,
38
+ marginTop: 8,
39
+ },
40
+ });
@@ -0,0 +1,15 @@
1
+ import { SvgXml } from 'react-native-svg';
2
+
3
+ const softwareMansionLogoXml = `<svg fill="currentColor" viewBox="0 0 149.79 80" xmlns="http://www.w3.org/2000/svg" width="150" height="80" preserveAspectRatio="xMidYMid meet"><path d="M24.281 79.063h124.58V24.356L125.513.937H.933v54.707z" fill="#fff"></path><path d="M0 .001h125.9l23.894 23.967V80h-125.9L.002 56.033V0zm1.867 3.198v52.057l21.48 21.545V24.744zm1.321-1.324 21.48 21.545h121.94l-21.48-21.545zm144.74 23.418H25.218v52.833h122.71z"></path><path d="M47.255 46.215c0 1.873-1.246 3.496-4.234 3.496-1.308 0-2.367-.312-3.3-.686v-2.623c.996.5 2.179.812 3.237.812.997 0 1.494-.312 1.494-.875 0-1.748-4.731-1.187-4.731-4.746 0-2.185 1.744-3.498 4.172-3.498.995 0 1.929.251 2.926.813v2.685c-1.308-.812-2.242-1.062-2.989-1.062-.872 0-1.37.313-1.37.874-.062 1.625 4.794 1 4.794 4.81z"></path><path d="M49.62 43.903c0-3.184 2.614-5.807 5.79-5.807 3.175 0 5.79 2.623 5.79 5.808 0 3.185-2.615 5.807-5.79 5.807-3.176-.062-5.79-2.622-5.79-5.807zm8.716 0c0-1.748-1.307-3.06-2.927-3.06-1.618 0-2.925 1.312-2.925 3.061 0 1.748 1.307 3.06 2.925 3.06 1.62 0 2.927-1.312 2.927-3.061z"></path><path d="M67.675 37.408v.937h3.674l-1.246 2.623h-2.366v8.493H65.06v-8.556h-1.867v-2.622h1.867v-1.061c0-3.373 1.744-5.059 4.483-5.059.685 0 1.307.125 1.868.25v2.623c-.498-.187-1.058-.25-1.62-.25-1.494 0-2.116 1-2.116 2.623z"></path><path d="M76.952 40.906v4.434c0 1.187.685 1.687 1.743 1.687.685 0 1.37-.188 1.93-.562v2.747c-.747.312-1.431.5-2.427.5-2.43 0-3.923-1.312-3.923-3.998v-4.746h-1.619v-2.622h1.62v-2.873l2.676-.687v3.56h3.674v2.622h-3.674z"></path><path d="m99.988 38.346-3.549 11.115h-2.677l-2.428-7.619-2.49 7.619h-2.678l-3.549-11.115h3.051l1.992 7.432 2.367-7.432h2.676l2.367 7.432 1.992-7.432z"></path><path d="M101.36 43.903c0-3.184 2.303-5.807 5.48-5.807 1.244 0 2.24.375 2.987 1v-.75h2.678v11.115h-2.616v-.874c-.747.687-1.805 1.124-3.112 1.124-3.052-.062-5.417-2.622-5.417-5.807zm8.717 0c0-1.748-1.308-3.06-2.927-3.06s-2.926 1.312-2.926 3.061c0 1.748 1.307 3.06 2.926 3.06 1.619 0 2.927-1.312 2.927-3.061z"></path><path d="M116.3 38.346h2.615v1.498c.81-1.498 2.303-1.748 3.611-1.748v3.184c-1.868-.5-3.548.562-3.548 2.936v5.183H116.3z"></path><path d="M130.99 47.089a9.627 9.627 0 0 0 3.549-.687l-1.37 2.81a7.296 7.296 0 0 1-2.677.5c-3.923 0-6.288-2.436-6.288-5.808 0-3.185 2.365-5.808 5.79-5.808 2.303 0 4.171 1.187 4.918 2.685v4.06h-7.844c.373 1.31 1.68 2.248 3.922 2.248zm-3.985-4.371h5.79c-.373-1.313-1.494-2.124-2.926-2.124-1.37 0-2.428.874-2.864 2.124z"></path><path d="M55.223 58.83v7.306h-2.677v-6.869c0-1.187-.872-1.873-1.743-1.873-.934 0-1.744.686-1.744 1.873v6.869h-2.676v-6.869c0-1.187-.872-1.873-1.744-1.873-.934 0-1.743.686-1.743 1.873v6.869H40.22V55.02h2.615v.812c.622-.75 1.432-1 2.49-1 1.183 0 2.18.5 2.864 1.437.871-1 1.992-1.436 3.362-1.436 2.054 0 3.673 1.623 3.673 3.996z"></path><path d="M58.025 60.579c0-3.186 2.304-5.808 5.478-5.808 1.246 0 2.243.374 2.989.999v-.75h2.677v11.115h-2.615v-.874c-.747.687-1.805 1.124-3.112 1.124-3.052 0-5.417-2.622-5.417-5.807zm8.717 0c0-1.75-1.308-3.061-2.927-3.061s-2.926 1.312-2.926 3.061c0 1.748 1.308 3.06 2.926 3.06s2.927-1.312 2.927-3.061z"></path><path d="M72.843 55.02h2.614v.812c.81-.812 1.868-1.062 2.927-1.062 2.304 0 4.17 1.748 4.17 4.122v7.18h-2.676v-6.556a2.103 2.103 0 0 0-2.117-2.123 2.102 2.102 0 0 0-2.116 2.123v6.62h-2.678V55.02h-.124z"></path><path d="M93.326 62.889c0 1.873-1.245 3.496-4.234 3.496-1.307 0-2.366-.312-3.299-.686v-2.623c.996.5 2.179.812 3.238.812.996 0 1.493-.312 1.493-.874 0-1.75-4.73-1.188-4.73-4.747 0-2.186 1.742-3.497 4.17-3.497.996 0 1.93.25 2.927.812v2.685c-1.308-.812-2.242-1.062-2.989-1.062-.872 0-1.37.312-1.37.874-.062 1.687 4.794 1.063 4.794 4.81z"></path><path d="M99.116 55.02v11.115h-2.677V55.02z"></path><path d="M101.92 60.579c0-3.186 2.615-5.808 5.79-5.808s5.79 2.622 5.79 5.807-2.615 5.807-5.79 5.807-5.79-2.622-5.79-5.807zm8.717 0c0-1.75-1.307-3.061-2.927-3.061-1.618 0-2.926 1.312-2.926 3.061 0 1.748 1.308 3.06 2.926 3.06 1.62 0 2.927-1.312 2.927-3.061z"></path><path d="M116.24 55.02h2.615v.812c.81-.812 1.868-1.062 2.927-1.062 2.303 0 4.17 1.748 4.17 4.122v7.18h-2.676v-6.556a2.103 2.103 0 0 0-2.118-2.123 2.103 2.103 0 0 0-2.116 2.123v6.62h-2.677V55.02h-.125z"></path><path d="M131.24 63.076v.437h-.933v2.623h-.499v-2.623h-.933v-.437z"></path><path d="m132.3 63.076.997 2.435.933-2.435h.623v3.06h-.498v-2.248l-.934 2.248h-.436l-.934-2.248v2.248h-.498v-3.061h.747z"></path></svg>`;
4
+
5
+ export function SoftwareMansionLogo({
6
+ width = 150,
7
+ height = 80,
8
+ }: {
9
+ width?: number;
10
+ height?: number;
11
+ }) {
12
+ return (
13
+ <SvgXml xml={softwareMansionLogoXml} width={width} height={height} accessibilityRole="image" />
14
+ );
15
+ }
@@ -0,0 +1,16 @@
1
+ import Svg, { Path } from 'react-native-svg';
2
+
3
+ export function SvgMark({ size = 44 }: { size?: number }) {
4
+ return (
5
+ <Svg width={size} height={size} viewBox="0 0 2048 2048" accessibilityRole="image">
6
+ <Path
7
+ fill="#5666ff"
8
+ d="m146.9 1305.8l-14.4 30q0 32.2 89.9 62.1 91 28.9 198.6 28.9 108.8 0 238.6-32.2 129.8-32.2 225.2-78.8 96.6-46.6 158.7-97.6 63.3-51.1 63.3-88.8 0-47.7-111-88.8-111-41-322.9-89.8-210.8-50-309.6-125.4-97.6-75.5-97.6-183.1 0-108.8 77.6-207.5 77.7-98.8 202-175.3 125.4-76.6 273-136.5 305.1-123.2 565.9-123.2 99.9 0 176.4 17.8 146.5 34.4 146.5 90.9 0 56.6-10 77.7-10 20-16.6 26.7-5.6 6.6-15.6 13.3-8.9 6.6-13.3 10-48.8 41-267.4 51-75.5 3.3-82.1 7.8-6.7 4.4-12.2 4.4-4.5 0-11.1-8.9-6.7-8.9-6.7-28.8 0-20 43.3-73.3-135.4 7.8-289.6 62.2-153.2 54.4-266.3 123.1-113.2 67.7-188.7 134.3-75.4 65.5-75.4 93.2 0 26.7 16.6 44.4 46.6 49.9 258.6 104.3 219.7 56.6 292.9 91 73.2 34.4 120.9 65.5 48.9 31 77.7 63.2 67.7 77.7 67.7 157.6 0 79.9-43.3 149.8-42.1 69.9-128.7 135.4-85.4 65.4-201.9 116.5-255.3 114.3-608.1 114.3-257.5 0-328.5-136.5-18.8-34.4-18.8-71 0-37.8 16.6-67.7 17.8-30 42.2-45.5 47.7-30 67.7-30 19.9 0 19.9 13.3z"
9
+ />
10
+ <Path
11
+ fill="#f66d22"
12
+ d="m486.9 1709.8l-14.4 30q0 32.2 89.9 62.1 91 28.9 198.6 28.9 108.8 0 238.6-32.2 129.8-32.2 225.2-78.8 96.6-46.6 158.7-97.6 63.3-51.1 63.3-88.8 0-47.7-111-88.8-111-41-322.9-89.8-210.8-50-309.6-125.4-97.6-75.5-97.6-183.1 0-108.8 77.6-207.5 77.7-98.8 202-175.3 125.4-76.6 273-136.5 305.1-123.2 565.9-123.2 99.9 0 176.4 17.8 146.5 34.4 146.5 90.9 0 56.6-10 77.7-10 20-16.6 26.7-5.6 6.6-15.6 13.3-8.9 6.6-13.3 10-48.8 41-267.4 51-75.5 3.3-82.1 7.8-6.7 4.4-12.2 4.4-4.5 0-11.1-8.9-6.7-8.9-6.7-28.8 0-20 43.3-73.3-135.4 7.8-289.6 62.2-153.2 54.4-266.3 123.1-113.2 67.7-188.7 134.3-75.4 65.5-75.4 93.2 0 26.7 16.6 44.4 46.6 49.9 258.6 104.3 219.7 56.6 292.9 91 73.2 34.4 120.9 65.5 48.9 31 77.7 63.2 67.7 77.7 67.7 157.6 0 79.9-43.3 149.8-42.1 69.9-128.7 135.4-85.4 65.4-201.9 116.5-255.3 114.3-608.1 114.3-257.5 0-328.5-136.5-18.8-34.4-18.8-71 0-37.8 16.6-67.7 17.8-30 42.2-45.5 47.7-30 67.7-30 19.9 0 19.9 13.3z"
13
+ />
14
+ </Svg>
15
+ );
16
+ }
@@ -0,0 +1,21 @@
1
+ export interface AppTask {
2
+ id: string;
3
+ title: string;
4
+ status: 'todo' | 'doing' | 'done';
5
+ }
6
+
7
+ export interface AppSnapshot {
8
+ name: string;
9
+ audience: string;
10
+ tasks: AppTask[];
11
+ }
12
+
13
+ export const appSnapshot: AppSnapshot = {
14
+ name: '[APP NAME]',
15
+ audience: '[TARGET AUDIENCE]',
16
+ tasks: [
17
+ { id: 'task-1', title: 'Shape the first user flow', status: 'doing' },
18
+ { id: 'task-2', title: 'Replace mock data with the real data layer', status: 'todo' },
19
+ { id: 'task-3', title: 'Run mds doctor before pushing', status: 'todo' },
20
+ ] satisfies AppTask[],
21
+ };