@echoform/echo-ui 0.1.0 → 0.2.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 (154) hide show
  1. package/README.md +57 -10
  2. package/dist/components/EchoChangeProfileNameModal.d.ts +59 -0
  3. package/dist/components/EchoChangeProfileNameModal.d.ts.map +1 -0
  4. package/dist/components/EchoChangeProfileNameModal.js +317 -0
  5. package/dist/components/EchoChangeProfileNameModal.js.map +1 -0
  6. package/dist/components/EchoColorPicker.d.ts +34 -0
  7. package/dist/components/EchoColorPicker.d.ts.map +1 -0
  8. package/dist/components/EchoColorPicker.js +206 -0
  9. package/dist/components/EchoColorPicker.js.map +1 -0
  10. package/dist/components/EchoConnectedAppsModal.d.ts +40 -0
  11. package/dist/components/EchoConnectedAppsModal.d.ts.map +1 -0
  12. package/dist/components/EchoConnectedAppsModal.js +220 -0
  13. package/dist/components/EchoConnectedAppsModal.js.map +1 -0
  14. package/dist/components/EchoDeleteProfilePanel.d.ts +63 -0
  15. package/dist/components/EchoDeleteProfilePanel.d.ts.map +1 -0
  16. package/dist/components/EchoDeleteProfilePanel.js +286 -0
  17. package/dist/components/EchoDeleteProfilePanel.js.map +1 -0
  18. package/dist/components/EchoDeletionIntercept.d.ts +39 -0
  19. package/dist/components/EchoDeletionIntercept.d.ts.map +1 -0
  20. package/dist/components/EchoDeletionIntercept.js +212 -0
  21. package/dist/components/EchoDeletionIntercept.js.map +1 -0
  22. package/dist/components/EchoDeletionStatusCard.d.ts +35 -0
  23. package/dist/components/EchoDeletionStatusCard.d.ts.map +1 -0
  24. package/dist/components/EchoDeletionStatusCard.js +156 -0
  25. package/dist/components/EchoDeletionStatusCard.js.map +1 -0
  26. package/dist/components/EchoEmblem.d.ts +93 -0
  27. package/dist/components/EchoEmblem.d.ts.map +1 -0
  28. package/dist/components/EchoEmblem.js +308 -0
  29. package/dist/components/EchoEmblem.js.map +1 -0
  30. package/dist/components/EchoEmblemPicker.d.ts +71 -0
  31. package/dist/components/EchoEmblemPicker.d.ts.map +1 -0
  32. package/dist/components/EchoEmblemPicker.js +490 -0
  33. package/dist/components/EchoEmblemPicker.js.map +1 -0
  34. package/dist/components/EchoErrorBanner.d.ts +17 -0
  35. package/dist/components/EchoErrorBanner.d.ts.map +1 -0
  36. package/dist/components/EchoErrorBanner.js +48 -0
  37. package/dist/components/EchoErrorBanner.js.map +1 -0
  38. package/dist/components/EchoFormField.d.ts +27 -0
  39. package/dist/components/EchoFormField.d.ts.map +1 -0
  40. package/dist/components/EchoFormField.js +49 -0
  41. package/dist/components/EchoFormField.js.map +1 -0
  42. package/dist/components/EchoLinkButton.d.ts +16 -0
  43. package/dist/components/EchoLinkButton.d.ts.map +1 -0
  44. package/dist/components/EchoLinkButton.js +37 -0
  45. package/dist/components/EchoLinkButton.js.map +1 -0
  46. package/dist/components/EchoMoreScreen.d.ts +41 -0
  47. package/dist/components/EchoMoreScreen.d.ts.map +1 -0
  48. package/dist/components/EchoMoreScreen.js +94 -0
  49. package/dist/components/EchoMoreScreen.js.map +1 -0
  50. package/dist/components/EchoPasswordField.d.ts +28 -0
  51. package/dist/components/EchoPasswordField.d.ts.map +1 -0
  52. package/dist/components/EchoPasswordField.js +67 -0
  53. package/dist/components/EchoPasswordField.js.map +1 -0
  54. package/dist/components/EchoPrimaryButton.d.ts +23 -0
  55. package/dist/components/EchoPrimaryButton.d.ts.map +1 -0
  56. package/dist/components/EchoPrimaryButton.js +54 -0
  57. package/dist/components/EchoPrimaryButton.js.map +1 -0
  58. package/dist/components/EchoProfileCard.d.ts +35 -0
  59. package/dist/components/EchoProfileCard.d.ts.map +1 -0
  60. package/dist/components/EchoProfileCard.js +206 -0
  61. package/dist/components/EchoProfileCard.js.map +1 -0
  62. package/dist/components/EchoProfileDivider.d.ts +14 -0
  63. package/dist/components/EchoProfileDivider.d.ts.map +1 -0
  64. package/dist/components/EchoProfileDivider.js +31 -0
  65. package/dist/components/EchoProfileDivider.js.map +1 -0
  66. package/dist/components/EchoProfileHero.d.ts +48 -0
  67. package/dist/components/EchoProfileHero.d.ts.map +1 -0
  68. package/dist/components/EchoProfileHero.js +215 -0
  69. package/dist/components/EchoProfileHero.js.map +1 -0
  70. package/dist/components/EchoProfileOverview.d.ts +18 -0
  71. package/dist/components/EchoProfileOverview.d.ts.map +1 -0
  72. package/dist/components/EchoProfileOverview.js +36 -0
  73. package/dist/components/EchoProfileOverview.js.map +1 -0
  74. package/dist/components/EchoProfileRow.d.ts +60 -0
  75. package/dist/components/EchoProfileRow.d.ts.map +1 -0
  76. package/dist/components/EchoProfileRow.js +113 -0
  77. package/dist/components/EchoProfileRow.js.map +1 -0
  78. package/dist/components/EchoProfileSection.d.ts +29 -0
  79. package/dist/components/EchoProfileSection.d.ts.map +1 -0
  80. package/dist/components/EchoProfileSection.js +58 -0
  81. package/dist/components/EchoProfileSection.js.map +1 -0
  82. package/dist/components/EchoRewardNotificationBanner.d.ts +61 -0
  83. package/dist/components/EchoRewardNotificationBanner.d.ts.map +1 -0
  84. package/dist/components/EchoRewardNotificationBanner.js +272 -0
  85. package/dist/components/EchoRewardNotificationBanner.js.map +1 -0
  86. package/dist/components/EchoSettingsRow.d.ts +24 -0
  87. package/dist/components/EchoSettingsRow.d.ts.map +1 -0
  88. package/dist/components/EchoSettingsRow.js +84 -0
  89. package/dist/components/EchoSettingsRow.js.map +1 -0
  90. package/dist/components/EchoSettingsSection.d.ts +16 -0
  91. package/dist/components/EchoSettingsSection.d.ts.map +1 -0
  92. package/dist/components/EchoSettingsSection.js +48 -0
  93. package/dist/components/EchoSettingsSection.js.map +1 -0
  94. package/dist/components/EchoSignOutButton.d.ts +28 -0
  95. package/dist/components/EchoSignOutButton.d.ts.map +1 -0
  96. package/dist/components/EchoSignOutButton.js +67 -0
  97. package/dist/components/EchoSignOutButton.js.map +1 -0
  98. package/dist/components/EchoSuccessBanner.d.ts +17 -0
  99. package/dist/components/EchoSuccessBanner.d.ts.map +1 -0
  100. package/dist/components/EchoSuccessBanner.js +48 -0
  101. package/dist/components/EchoSuccessBanner.js.map +1 -0
  102. package/dist/components/EchoSyncPanel.d.ts +165 -0
  103. package/dist/components/EchoSyncPanel.d.ts.map +1 -0
  104. package/dist/components/EchoSyncPanel.js +913 -0
  105. package/dist/components/EchoSyncPanel.js.map +1 -0
  106. package/dist/components/EchoSyncPromptModal.d.ts +87 -0
  107. package/dist/components/EchoSyncPromptModal.d.ts.map +1 -0
  108. package/dist/components/EchoSyncPromptModal.js +351 -0
  109. package/dist/components/EchoSyncPromptModal.js.map +1 -0
  110. package/dist/components/EchoTitlePicker.d.ts +72 -0
  111. package/dist/components/EchoTitlePicker.d.ts.map +1 -0
  112. package/dist/components/EchoTitlePicker.js +487 -0
  113. package/dist/components/EchoTitlePicker.js.map +1 -0
  114. package/dist/components/EchoVerificationCodeInput.d.ts +40 -0
  115. package/dist/components/EchoVerificationCodeInput.d.ts.map +1 -0
  116. package/dist/components/EchoVerificationCodeInput.js +140 -0
  117. package/dist/components/EchoVerificationCodeInput.js.map +1 -0
  118. package/dist/components/EchoWelcome.d.ts +44 -0
  119. package/dist/components/EchoWelcome.d.ts.map +1 -0
  120. package/dist/components/EchoWelcome.js +289 -0
  121. package/dist/components/EchoWelcome.js.map +1 -0
  122. package/dist/components/EchoWhyBecomeAnEchoModal.d.ts +34 -0
  123. package/dist/components/EchoWhyBecomeAnEchoModal.d.ts.map +1 -0
  124. package/dist/components/EchoWhyBecomeAnEchoModal.js +321 -0
  125. package/dist/components/EchoWhyBecomeAnEchoModal.js.map +1 -0
  126. package/dist/components/EchoformInfoButton.d.ts +27 -0
  127. package/dist/components/EchoformInfoButton.d.ts.map +1 -0
  128. package/dist/components/EchoformInfoButton.js +60 -0
  129. package/dist/components/EchoformInfoButton.js.map +1 -0
  130. package/dist/components/EchoformInfoModal.d.ts +63 -0
  131. package/dist/components/EchoformInfoModal.d.ts.map +1 -0
  132. package/dist/components/EchoformInfoModal.js +347 -0
  133. package/dist/components/EchoformInfoModal.js.map +1 -0
  134. package/dist/echoColors.d.ts +58 -0
  135. package/dist/echoColors.d.ts.map +1 -0
  136. package/dist/echoColors.js +94 -0
  137. package/dist/echoColors.js.map +1 -0
  138. package/dist/index.d.ts +71 -3
  139. package/dist/index.d.ts.map +1 -1
  140. package/dist/index.js +86 -4
  141. package/dist/index.js.map +1 -1
  142. package/dist/theme.d.ts +48 -0
  143. package/dist/theme.d.ts.map +1 -0
  144. package/dist/theme.js +51 -0
  145. package/dist/theme.js.map +1 -0
  146. package/dist/titleRarity.d.ts +19 -0
  147. package/dist/titleRarity.d.ts.map +1 -0
  148. package/dist/titleRarity.js +46 -0
  149. package/dist/titleRarity.js.map +1 -0
  150. package/package.json +6 -5
  151. package/dist/components/EchoUITest.d.ts +0 -14
  152. package/dist/components/EchoUITest.d.ts.map +0 -1
  153. package/dist/components/EchoUITest.js +0 -53
  154. package/dist/components/EchoUITest.js.map +0 -1
@@ -0,0 +1,913 @@
1
+ "use strict";
2
+ /**
3
+ * EchoSyncPanel
4
+ *
5
+ * Presentation-only Echo Profile Sync panel.
6
+ * Extracted from components/feature/SyncPanel.tsx in the Echo UI Reference.
7
+ *
8
+ * ARCHITECTURE
9
+ * ────────────
10
+ * This component is PURE PRESENTATION. It owns:
11
+ * • Rendering all sync states (signed-out, loading, active, result)
12
+ * • Deriving button/badge display state from supplied props
13
+ * • Accessibility labels, roles, live regions
14
+ *
15
+ * It does NOT own:
16
+ * • Authentication
17
+ * • useFocusEffect
18
+ * • Server/status fetching
19
+ * • Upload / download operations
20
+ * • Reconciliation planning
21
+ * • Guest-partition detection or cleanup
22
+ * • Sync queue operations
23
+ * • Storage operations
24
+ * • Supabase / EchoBrain RPC calls
25
+ *
26
+ * All host-owned operations are supplied via callback props.
27
+ *
28
+ * DATA LABEL
29
+ * ──────────
30
+ * The `dataLabel` prop names the records being synchronized in ALL visible
31
+ * and accessibility wording. Examples: "Demo App Data", "Rig Vault Data".
32
+ * There is no hard-coded "Demo App", "Rig Vault", or "Guest Vault" in this
33
+ * package component.
34
+ *
35
+ * GUEST ADOPTION
36
+ * ──────────────
37
+ * All wording for the guest-adoption card is supplied through the optional
38
+ * `guestAdoption` prop. When neither `guestAdoption` nor `onAdoptGuest` is
39
+ * supplied the section is simply not rendered.
40
+ *
41
+ * DEVELOPMENT CONTROLS
42
+ * ────────────────────
43
+ * `showDevelopmentControls` replaces `__DEV__` so the host decides when
44
+ * dev-only UI (Force Sync, raw conflict JSON) is visible.
45
+ *
46
+ * SIGNED-OUT STATE
47
+ * ────────────────
48
+ * Deliberately names EchoBrain in the signed-out message:
49
+ * "Echo Profile Sync requires an Echo Profile.
50
+ * Sign in to enable upload to EchoBrain."
51
+ * This is an intentional product-level decision; EchoBrain is the public
52
+ * cloud sync service name.
53
+ *
54
+ * No Expo Router, no auth context, no services, no AsyncStorage, no Supabase,
55
+ * no @/ aliases.
56
+ * Canonical shared component for @echoform/echo-ui.
57
+ */
58
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
59
+ if (k2 === undefined) k2 = k;
60
+ var desc = Object.getOwnPropertyDescriptor(m, k);
61
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
62
+ desc = { enumerable: true, get: function() { return m[k]; } };
63
+ }
64
+ Object.defineProperty(o, k2, desc);
65
+ }) : (function(o, m, k, k2) {
66
+ if (k2 === undefined) k2 = k;
67
+ o[k2] = m[k];
68
+ }));
69
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
70
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
71
+ }) : function(o, v) {
72
+ o["default"] = v;
73
+ });
74
+ var __importStar = (this && this.__importStar) || (function () {
75
+ var ownKeys = function(o) {
76
+ ownKeys = Object.getOwnPropertyNames || function (o) {
77
+ var ar = [];
78
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
79
+ return ar;
80
+ };
81
+ return ownKeys(o);
82
+ };
83
+ return function (mod) {
84
+ if (mod && mod.__esModule) return mod;
85
+ var result = {};
86
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
87
+ __setModuleDefault(result, mod);
88
+ return result;
89
+ };
90
+ })();
91
+ Object.defineProperty(exports, "__esModule", { value: true });
92
+ exports.EchoSyncPanel = EchoSyncPanel;
93
+ const react_1 = __importStar(require("react"));
94
+ const react_native_1 = require("react-native");
95
+ const vector_icons_1 = require("@expo/vector-icons");
96
+ const theme_1 = require("../theme");
97
+ // ─────────────────────────────────────────────────────────────────────────────
98
+ // Internal sub-components
99
+ // ─────────────────────────────────────────────────────────────────────────────
100
+ function StatusBadge({ count, label, color, }) {
101
+ return (<react_native_1.View style={[
102
+ styles.badge,
103
+ { borderColor: color + '44', backgroundColor: color + '15' },
104
+ ]} accessible accessibilityLabel={`${count} ${label}`}>
105
+ <react_native_1.Text style={[styles.badgeCount, { color }]}>{count}</react_native_1.Text>
106
+ <react_native_1.Text style={[styles.badgeLabel, { color: color + 'CC' }]}>{label}</react_native_1.Text>
107
+ </react_native_1.View>);
108
+ }
109
+ function ConflictDetailRow({ conflict, showRaw, }) {
110
+ var _a, _b;
111
+ const [rawExpanded, setRawExpanded] = (0, react_1.useState)(false);
112
+ return (<react_native_1.View style={styles.conflictBlock}>
113
+ <react_native_1.View style={styles.detailRow}>
114
+ <vector_icons_1.MaterialIcons name="warning-amber" size={13} color={theme_1.COLORS.warning}/>
115
+ <react_native_1.Text style={[styles.detailText, { color: theme_1.COLORS.warning, fontWeight: '700' }]} selectable>
116
+ CONFLICT: {conflict.reason}
117
+ </react_native_1.Text>
118
+ </react_native_1.View>
119
+ <react_native_1.View style={styles.conflictField}>
120
+ <react_native_1.Text style={styles.conflictLabel}>record_id</react_native_1.Text>
121
+ <react_native_1.Text style={styles.conflictValue} selectable>
122
+ {conflict.id}
123
+ </react_native_1.Text>
124
+ </react_native_1.View>
125
+ {conflict.presetId ? (<react_native_1.View style={styles.conflictField}>
126
+ <react_native_1.Text style={styles.conflictLabel}>preset_id</react_native_1.Text>
127
+ <react_native_1.Text style={styles.conflictValue} selectable>
128
+ {conflict.presetId}
129
+ </react_native_1.Text>
130
+ </react_native_1.View>) : null}
131
+ {conflict.controlId ? (<react_native_1.View style={styles.conflictField}>
132
+ <react_native_1.Text style={styles.conflictLabel}>control_id</react_native_1.Text>
133
+ <react_native_1.Text style={styles.conflictValue} selectable>
134
+ {conflict.controlId}
135
+ </react_native_1.Text>
136
+ </react_native_1.View>) : null}
137
+ {conflict.serverRevision !== undefined ||
138
+ conflict.submittedBaseRevision !== undefined ? (<react_native_1.View style={styles.conflictField}>
139
+ <react_native_1.Text style={styles.conflictLabel}>revisions</react_native_1.Text>
140
+ <react_native_1.Text style={styles.conflictValue} selectable>
141
+ {`local sent: ${(_a = conflict.submittedBaseRevision) !== null && _a !== void 0 ? _a : '?'} · server has: ${(_b = conflict.serverRevision) !== null && _b !== void 0 ? _b : '?'}`}
142
+ </react_native_1.Text>
143
+ </react_native_1.View>) : null}
144
+ {showRaw && conflict.raw ? (<react_native_1.Pressable onPress={() => setRawExpanded(v => !v)} style={styles.rawToggle} accessibilityRole="button" accessibilityLabel={rawExpanded ? 'Collapse raw conflict JSON' : 'Expand raw conflict JSON'} accessibilityState={{ expanded: rawExpanded }} hitSlop={6}>
145
+ <vector_icons_1.MaterialIcons name={rawExpanded ? 'expand-less' : 'expand-more'} size={12} color={theme_1.COLORS.textMuted}/>
146
+ <react_native_1.Text style={styles.rawToggleLabel}>raw JSON</react_native_1.Text>
147
+ </react_native_1.Pressable>) : null}
148
+ {showRaw && rawExpanded && conflict.raw ? (<react_native_1.Text style={styles.rawJson} selectable>
149
+ {conflict.raw}
150
+ </react_native_1.Text>) : null}
151
+ </react_native_1.View>);
152
+ }
153
+ function EntityRow({ entity, uploaded, skipped, conflicts, failed, errors, conflictDetails, firstError, showDevelopmentControls, }) {
154
+ const [expanded, setExpanded] = (0, react_1.useState)(false);
155
+ const hasDetails = Object.keys(errors).length > 0 ||
156
+ conflictDetails.length > 0 ||
157
+ Boolean(firstError);
158
+ const total = uploaded + skipped + conflicts + failed;
159
+ if (total === 0 && !hasDetails)
160
+ return null;
161
+ const label = entity.replace(/_/g, ' ');
162
+ return (<react_native_1.View style={styles.entityRow}>
163
+ <react_native_1.Pressable onPress={hasDetails ? () => setExpanded(v => !v) : undefined} style={({ pressed }) => [
164
+ styles.entityRowHeader,
165
+ pressed && hasDetails && { opacity: 0.75 },
166
+ ]} accessibilityLabel={`${label}: ${uploaded} uploaded, ${skipped} skipped, ${conflicts} conflicts, ${failed} failed`} accessibilityRole={hasDetails ? 'button' : 'text'} accessibilityHint={hasDetails
167
+ ? `Double tap to ${expanded ? 'collapse' : 'expand'} error details`
168
+ : undefined} accessibilityState={hasDetails ? { expanded } : undefined}>
169
+ <react_native_1.Text style={styles.entityName}>{label}</react_native_1.Text>
170
+ <react_native_1.View style={styles.entityCounts}>
171
+ {uploaded > 0 ? (<react_native_1.Text style={[styles.entityCount, { color: theme_1.COLORS.neonGreen }]}>
172
+ ↑{uploaded}
173
+ </react_native_1.Text>) : null}
174
+ {skipped > 0 ? (<react_native_1.Text style={[styles.entityCount, { color: theme_1.COLORS.textMuted }]}>
175
+ ={skipped}
176
+ </react_native_1.Text>) : null}
177
+ {conflicts > 0 ? (<react_native_1.Text style={[styles.entityCount, { color: theme_1.COLORS.warning }]}>
178
+ ⚡{conflicts}
179
+ </react_native_1.Text>) : null}
180
+ {failed > 0 ? (<react_native_1.Text style={[styles.entityCount, { color: theme_1.COLORS.error }]}>
181
+ ✗{failed}
182
+ </react_native_1.Text>) : null}
183
+ </react_native_1.View>
184
+ {hasDetails ? (<vector_icons_1.MaterialIcons name={expanded ? 'expand-less' : 'expand-more'} size={18} color={theme_1.COLORS.textMuted}/>) : null}
185
+ </react_native_1.Pressable>
186
+
187
+ {expanded && hasDetails ? (<react_native_1.View style={styles.entityDetails} accessibilityLiveRegion="polite">
188
+ {firstError ? (<react_native_1.View style={styles.detailRow}>
189
+ <vector_icons_1.MaterialIcons name="bug-report" size={13} color={theme_1.COLORS.error}/>
190
+ <react_native_1.Text style={[styles.detailText, { color: theme_1.COLORS.error }]} selectable>
191
+ RPC error: {firstError}
192
+ </react_native_1.Text>
193
+ </react_native_1.View>) : null}
194
+ {Object.entries(errors).map(([id, msg]) => (<react_native_1.View key={id} style={styles.detailRow}>
195
+ <vector_icons_1.MaterialIcons name="error-outline" size={13} color={theme_1.COLORS.error}/>
196
+ <react_native_1.Text style={styles.detailText} selectable>
197
+ <react_native_1.Text style={styles.detailId}>{id.slice(0, 8)}…</react_native_1.Text>: {msg}
198
+ </react_native_1.Text>
199
+ </react_native_1.View>))}
200
+ {conflictDetails.map(c => (<ConflictDetailRow key={c.id} conflict={c} showRaw={showDevelopmentControls}/>))}
201
+ </react_native_1.View>) : null}
202
+ </react_native_1.View>);
203
+ }
204
+ // ─────────────────────────────────────────────────────────────────────────────
205
+ // Result card helpers
206
+ // ─────────────────────────────────────────────────────────────────────────────
207
+ function resultCardStyle(outcome) {
208
+ switch (outcome) {
209
+ case 'adoption_complete':
210
+ case 'complete':
211
+ case 'empty':
212
+ return styles.resultCardSuccess;
213
+ case 'partial':
214
+ case 'unable_to_verify':
215
+ return styles.resultCardWarning;
216
+ case 'failed':
217
+ case 'fatal':
218
+ return styles.resultCardError;
219
+ }
220
+ }
221
+ function resultIconName(outcome) {
222
+ switch (outcome) {
223
+ case 'adoption_complete':
224
+ case 'complete':
225
+ case 'empty':
226
+ return 'check-circle-outline';
227
+ case 'partial':
228
+ case 'unable_to_verify':
229
+ return 'warning-amber';
230
+ case 'failed':
231
+ case 'fatal':
232
+ return 'error-outline';
233
+ }
234
+ }
235
+ function resultIconColor(outcome) {
236
+ switch (outcome) {
237
+ case 'adoption_complete':
238
+ case 'complete':
239
+ case 'empty':
240
+ return theme_1.COLORS.neonGreen;
241
+ case 'partial':
242
+ case 'unable_to_verify':
243
+ return theme_1.COLORS.warning;
244
+ case 'failed':
245
+ case 'fatal':
246
+ return theme_1.COLORS.error;
247
+ }
248
+ }
249
+ function buildResultHeadline(result, dataLabel) {
250
+ switch (result.outcome) {
251
+ case 'adoption_complete':
252
+ return `${dataLabel} added to Echo Profile Sync.`;
253
+ case 'complete':
254
+ return `${dataLabel} Sync Complete`;
255
+ case 'empty':
256
+ return `${dataLabel} Up to Date`;
257
+ case 'partial':
258
+ return `${dataLabel} Sync Partially Completed`;
259
+ case 'failed':
260
+ return `${dataLabel} Sync Failed`;
261
+ case 'fatal':
262
+ return result.fatalError
263
+ ? `${dataLabel} Sync Failed: ${result.fatalError}`
264
+ : `${dataLabel} Sync Failed`;
265
+ case 'unable_to_verify':
266
+ return `Unable to Verify ${dataLabel} Sync`;
267
+ }
268
+ }
269
+ function buildResultA11yLabel(result, dataLabel) {
270
+ var _a, _b;
271
+ const remaining = result.remainingUnsynced && result.remainingUnsynced > 0
272
+ ? ` ${result.remainingUnsynced} records still need synchronization.`
273
+ : '';
274
+ switch (result.outcome) {
275
+ case 'adoption_complete':
276
+ return `${dataLabel} successfully added to Echo Profile Sync.${remaining}`;
277
+ case 'complete':
278
+ return `${dataLabel} sync complete. ${result.totalUploaded} uploaded, ${result.totalSkipped} already synced.${remaining}`;
279
+ case 'empty':
280
+ return `${dataLabel} is already up to date.${remaining}`;
281
+ case 'partial':
282
+ return `${dataLabel} sync partially completed. ${result.totalUploaded} uploaded, ${result.totalFailed} failed.${remaining}`;
283
+ case 'failed':
284
+ return `${dataLabel} sync failed. ${(_a = result.firstError) !== null && _a !== void 0 ? _a : 'See error details below.'}${remaining}`;
285
+ case 'fatal':
286
+ return `${dataLabel} sync failed. ${(_b = result.fatalError) !== null && _b !== void 0 ? _b : 'See error details below.'}${remaining}`;
287
+ case 'unable_to_verify':
288
+ return `Unable to verify ${dataLabel} sync.${remaining}`;
289
+ }
290
+ }
291
+ // ─────────────────────────────────────────────────────────────────────────────
292
+ // Main component
293
+ // ─────────────────────────────────────────────────────────────────────────────
294
+ function EchoSyncPanel({ hasEchoProfile, dataLabel, status, statusLoading = false, syncing = false, guestAdoption, result, showDevelopmentControls = false, onRefresh, onSync, onForceSync, onAdoptGuest, onDismissResult, }) {
295
+ var _a, _b, _c, _d;
296
+ // ── Signed-out state ────────────────────────────────────────────────────
297
+ if (!hasEchoProfile) {
298
+ return (<react_native_1.View style={styles.guestNotice} accessible accessibilityRole="text" accessibilityLabel="Echo Profile Sync requires an Echo Profile. Sign in or create an Echo Profile to enable upload to EchoBrain.">
299
+ <vector_icons_1.MaterialIcons name="cloud-off" size={20} color={theme_1.COLORS.textMuted}/>
300
+ <react_native_1.Text style={styles.guestNoticeText}>
301
+ {'Echo Profile Sync requires an Echo Profile.\n'}
302
+ {'Sign in to enable upload to EchoBrain.'}
303
+ </react_native_1.Text>
304
+ </react_native_1.View>);
305
+ }
306
+ // ── Derived display state ───────────────────────────────────────────────
307
+ const unsyncedCount = (_a = status === null || status === void 0 ? void 0 : status.unsynced) !== null && _a !== void 0 ? _a : null;
308
+ const needsSync = unsyncedCount !== null && unsyncedCount > 0;
309
+ // canVerify: only true when we have a confirmed, reachable server response.
310
+ // status === null (no fetch yet) is treated as unable to verify, not reachable.
311
+ const canVerify = status !== null && status.serverReachable;
312
+ const syncDisabled = syncing || (canVerify && !needsSync);
313
+ // ── Refresh handler with accessibility announcement ─────────────────────
314
+ const handleRefresh = async () => {
315
+ if (statusLoading)
316
+ return;
317
+ await onRefresh();
318
+ react_native_1.AccessibilityInfo.announceForAccessibility(`${dataLabel} sync status refreshed.`);
319
+ };
320
+ // ── Render ───────────────────────────────────────────────────────────────
321
+ return (<react_native_1.View style={styles.root}>
322
+
323
+ {/* ── Scope note ──────────────────────────────────────────────────── */}
324
+ <react_native_1.Text style={styles.scopeNote} accessible accessibilityRole="text" accessibilityLabel={`This section shows and syncs ${dataLabel} only.`}>
325
+ {`This section shows and syncs ${dataLabel} only.`}
326
+ </react_native_1.Text>
327
+
328
+ {/* ── Status summary row ──────────────────────────────────────────── */}
329
+ <react_native_1.View style={styles.statusRow} accessible accessibilityLabel={status
330
+ ? `${dataLabel} sync status: ${status.unsynced} unsynced, ${status.synced} synced` +
331
+ (!status.serverReachable ? ', Echo Profile Sync unavailable' : '')
332
+ : `Loading ${dataLabel} sync status`}>
333
+ {statusLoading ? (<react_native_1.ActivityIndicator size="small" color={theme_1.COLORS.neonCyan} style={{ marginRight: theme_1.SPACING.sm }}/>) : status ? (<>
334
+ <StatusBadge count={status.unsynced} label="unsynced" color={status.unsynced > 0 ? theme_1.COLORS.neonCyan : theme_1.COLORS.textMuted}/>
335
+ <StatusBadge count={status.synced} label="synced" color={theme_1.COLORS.neonGreen}/>
336
+ {!status.serverReachable ? (<react_native_1.View style={styles.offlineTag} accessible accessibilityLabel={`Echo Profile Sync unavailable — ${dataLabel} sync status unverified`}>
337
+ <vector_icons_1.MaterialIcons name="cloud-off" size={11} color={theme_1.COLORS.warning}/>
338
+ <react_native_1.Text style={[styles.offlineTagText, { color: theme_1.COLORS.warning }]}>
339
+ offline
340
+ </react_native_1.Text>
341
+ </react_native_1.View>) : null}
342
+ </>) : null}
343
+
344
+ <react_native_1.Pressable onPress={handleRefresh} disabled={statusLoading} style={({ pressed }) => [
345
+ styles.refreshBtn,
346
+ pressed && !statusLoading && { opacity: 0.7 },
347
+ ]} accessibilityLabel={`Refresh ${dataLabel} Sync Status`} accessibilityRole="button" accessibilityHint={`Checks for changes and updates the ${dataLabel} sync status.`} accessibilityState={{ busy: statusLoading, disabled: statusLoading }} hitSlop={12}>
348
+ {statusLoading ? (<react_native_1.ActivityIndicator size="small" color={theme_1.COLORS.neonCyan}/>) : (<vector_icons_1.MaterialIcons name="refresh" size={20} color={theme_1.COLORS.neonCyan} importantForAccessibility="no" accessibilityElementsHidden/>)}
349
+ </react_native_1.Pressable>
350
+ </react_native_1.View>
351
+
352
+ {/* ── Main sync button ─────────────────────────────────────────────── */}
353
+ <react_native_1.Pressable onPress={() => onSync()} disabled={syncDisabled} style={({ pressed }) => [
354
+ styles.uploadBtn,
355
+ pressed && { opacity: 0.8 },
356
+ needsSync || !canVerify ? styles.uploadBtnActive : styles.uploadBtnDisabled,
357
+ !canVerify && !syncing ? styles.uploadBtnWarning : null,
358
+ ]} accessibilityLabel={syncing
359
+ ? `Syncing ${dataLabel}, please wait`
360
+ : !canVerify
361
+ ? `Unable to verify ${dataLabel} sync — tap to retry`
362
+ : !needsSync
363
+ ? `${dataLabel} is up to date`
364
+ : `Sync ${dataLabel} — ${unsyncedCount} record${unsyncedCount !== 1 ? 's' : ''} need synchronization`} accessibilityRole="button" accessibilityHint={!canVerify
365
+ ? `Echo Profile Sync could not be reached to verify ${dataLabel} sync state. Tap to retry.`
366
+ : needsSync
367
+ ? `Uploads pending ${dataLabel} changes and downloads newer ${dataLabel} cloud records through Echo Profile Sync`
368
+ : `No ${dataLabel} changes need to be synchronized right now`} accessibilityState={{ busy: syncing, disabled: syncDisabled }}>
369
+ {syncing ? (<react_native_1.ActivityIndicator size="small" color={theme_1.COLORS.neonGreen}/>) : (<vector_icons_1.MaterialIcons name={!canVerify ? 'cloud-off' : needsSync ? 'sync' : 'cloud-done'} size={20} color={!canVerify
370
+ ? theme_1.COLORS.warning
371
+ : needsSync
372
+ ? theme_1.COLORS.neonGreen
373
+ : theme_1.COLORS.textMuted}/>)}
374
+ <react_native_1.Text style={[
375
+ styles.uploadBtnText,
376
+ canVerify && !needsSync ? styles.uploadBtnTextDisabled : null,
377
+ !canVerify && !syncing ? styles.uploadBtnTextWarning : null,
378
+ ]}>
379
+ {syncing
380
+ ? `Syncing ${dataLabel}\u2026`
381
+ : !canVerify
382
+ ? `Unable to Verify ${dataLabel} Sync`
383
+ : needsSync
384
+ ? `Sync ${dataLabel} (${unsyncedCount})`
385
+ : `${dataLabel} Up to Date`}
386
+ </react_native_1.Text>
387
+ </react_native_1.Pressable>
388
+
389
+ {/* ── Force Sync (dev only, host-controlled) ─────────────────────── */}
390
+ {showDevelopmentControls && onForceSync ? (<react_native_1.Pressable onPress={() => onForceSync()} disabled={syncing} style={({ pressed }) => [styles.forceBtn, pressed && { opacity: 0.7 }]} accessibilityLabel="Force full re-sync — upload all then download" accessibilityRole="button" accessibilityHint="Development only: re-uploads all local records regardless of sync status, then downloads" accessibilityState={{ busy: syncing }}>
391
+ <vector_icons_1.MaterialIcons name="refresh" size={14} color={theme_1.COLORS.textMuted}/>
392
+ <react_native_1.Text style={styles.forceBtnText}>Force full re-sync (dev)</react_native_1.Text>
393
+ </react_native_1.Pressable>) : null}
394
+
395
+ {/* ── Guest-data adoption ─────────────────────────────────────────── */}
396
+ {guestAdoption && onAdoptGuest ? (<react_native_1.View style={styles.guestAdoptSection}>
397
+ <react_native_1.View style={styles.dividerRow} accessible={false}>
398
+ <react_native_1.View style={styles.dividerLine}/>
399
+ <react_native_1.Text style={styles.dividerLabel}>OR</react_native_1.Text>
400
+ <react_native_1.View style={styles.dividerLine}/>
401
+ </react_native_1.View>
402
+
403
+ <react_native_1.View style={styles.guestAdoptCard}>
404
+ <react_native_1.View style={styles.guestAdoptHeader}>
405
+ <vector_icons_1.MaterialIcons name="account-circle" size={15} color={theme_1.COLORS.neonCyan}/>
406
+ <react_native_1.Text style={styles.guestAdoptTitle} accessibilityRole="header">
407
+ {guestAdoption.title}
408
+ </react_native_1.Text>
409
+ </react_native_1.View>
410
+
411
+ <react_native_1.Text style={styles.guestAdoptSubtitle}>
412
+ {guestAdoption.description}
413
+ </react_native_1.Text>
414
+
415
+ <react_native_1.Pressable onPress={() => onAdoptGuest()} disabled={syncing} style={({ pressed }) => [
416
+ styles.guestAdoptBtn,
417
+ pressed && !syncing && { opacity: 0.8 },
418
+ syncing && styles.uploadBtnDisabled,
419
+ ]} accessibilityLabel={syncing
420
+ ? `Adding ${guestAdoption.title}, please wait`
421
+ : guestAdoption.accessibilityLabel} accessibilityRole="button" accessibilityHint={guestAdoption.accessibilityHint} accessibilityState={{ busy: syncing, disabled: syncing }}>
422
+ {syncing ? (<react_native_1.ActivityIndicator size="small" color={theme_1.COLORS.neonCyan}/>) : (<vector_icons_1.MaterialIcons name="cloud-upload" size={20} color={theme_1.COLORS.neonCyan}/>)}
423
+ <react_native_1.Text style={styles.guestAdoptBtnText}>
424
+ {syncing
425
+ ? ((_b = guestAdoption.loadingLabel) !== null && _b !== void 0 ? _b : `${guestAdoption.buttonLabel}\u2026`)
426
+ : guestAdoption.buttonLabel}
427
+ </react_native_1.Text>
428
+ </react_native_1.Pressable>
429
+
430
+ <react_native_1.View style={styles.guestAdoptNote} accessible accessibilityRole="text" accessibilityLabel={guestAdoption.note}>
431
+ <vector_icons_1.MaterialIcons name="info-outline" size={13} color={theme_1.COLORS.neonBlue}/>
432
+ <react_native_1.Text style={styles.guestAdoptNoteText}>{guestAdoption.note}</react_native_1.Text>
433
+ </react_native_1.View>
434
+ </react_native_1.View>
435
+ </react_native_1.View>) : null}
436
+
437
+ {/* ── Sync result card ─────────────────────────────────────────────── */}
438
+ {result ? (<react_native_1.View style={[styles.resultCard, resultCardStyle(result.outcome)]} accessible accessibilityRole="text" accessibilityLiveRegion="polite" accessibilityLabel={buildResultA11yLabel(result, dataLabel)}>
439
+ {/* Headline */}
440
+ <react_native_1.View style={styles.resultHeader}>
441
+ <vector_icons_1.MaterialIcons name={resultIconName(result.outcome)} size={18} color={resultIconColor(result.outcome)}/>
442
+ <react_native_1.Text style={[styles.resultTitle, { color: resultIconColor(result.outcome) }]}>
443
+ {buildResultHeadline(result, dataLabel)}
444
+ </react_native_1.Text>
445
+ </react_native_1.View>
446
+
447
+ {/* First RPC error */}
448
+ {(result.outcome === 'failed' || result.outcome === 'partial') &&
449
+ result.firstError ? (<react_native_1.View style={styles.rpcErrorBox}>
450
+ <vector_icons_1.MaterialIcons name="bug-report" size={13} color={theme_1.COLORS.error}/>
451
+ <react_native_1.Text style={styles.rpcErrorText} selectable>
452
+ {result.firstError}
453
+ </react_native_1.Text>
454
+ </react_native_1.View>) : null}
455
+
456
+ {/* Counts summary */}
457
+ {result.outcome !== 'fatal' ? (<react_native_1.Text style={styles.resultLegend}>
458
+ {`↑${result.totalUploaded} uploaded`}
459
+ {result.totalSkipped > 0
460
+ ? ` =${result.totalSkipped} already synced`
461
+ : ''}
462
+ {result.totalConflicts > 0
463
+ ? ` ⚡${result.totalConflicts} conflict`
464
+ : ''}
465
+ {result.totalFailed > 0 ? ` ✗${result.totalFailed} failed` : ''}
466
+ </react_native_1.Text>) : null}
467
+
468
+ {/* Source partition (dev diagnostic) */}
469
+ {showDevelopmentControls && result.sourcePartition ? (<react_native_1.Text style={[styles.resultLegend, { color: theme_1.COLORS.neonCyan + '99' }]}>
470
+ Source partition: {result.sourcePartition}
471
+ </react_native_1.Text>) : null}
472
+
473
+ {/* Remaining unsynced */}
474
+ {result.remainingUnsynced && result.remainingUnsynced > 0 ? (<react_native_1.Text style={[styles.resultLegend, { color: theme_1.COLORS.warning }]}>
475
+ {`${result.remainingUnsynced} record${result.remainingUnsynced !== 1 ? 's' : ''} still need synchronization`}
476
+ </react_native_1.Text>) : null}
477
+
478
+ {/* Image sync summary */}
479
+ {result.imageSummary && result.imageSummary.attempted > 0 ? (<react_native_1.View style={styles.imageSyncRow}>
480
+ <vector_icons_1.MaterialIcons name={result.imageSummary.failed > 0 ? 'broken-image' : 'photo-camera'} size={12} color={result.imageSummary.failed > 0
481
+ ? theme_1.COLORS.error
482
+ : theme_1.COLORS.neonGreen}/>
483
+ <react_native_1.Text style={[styles.resultLegend, { flex: 1 }]}>
484
+ {`Photos: ${result.imageSummary.succeeded}/${result.imageSummary.attempted} uploaded`}
485
+ {result.imageSummary.failed > 0
486
+ ? ` ✗${result.imageSummary.failed} failed`
487
+ : ''}
488
+ </react_native_1.Text>
489
+ </react_native_1.View>) : result.imageSummary && result.imageSummary.attempted === 0 ? (<react_native_1.View style={styles.imageSyncRow}>
490
+ <vector_icons_1.MaterialIcons name="photo-camera" size={12} color={theme_1.COLORS.textMuted}/>
491
+ <react_native_1.Text style={[styles.resultLegend, { flex: 1 }]}>
492
+ {'Photos: 0 eligible (no local photos found)'}
493
+ </react_native_1.Text>
494
+ </react_native_1.View>) : null}
495
+
496
+ {/* First image error */}
497
+ {((_c = result.imageSummary) === null || _c === void 0 ? void 0 : _c.failed) ? (<react_native_1.View style={styles.rpcErrorBox}>
498
+ <vector_icons_1.MaterialIcons name="broken-image" size={13} color={theme_1.COLORS.error}/>
499
+ <react_native_1.Text style={styles.rpcErrorText} selectable>
500
+ {`Photo error: ${(_d = result.imageSummary.firstError) !== null && _d !== void 0 ? _d : 'unknown'}${result.imageSummary.timeoutFailures
501
+ ? ` (${result.imageSummary.timeoutFailures} timed out)`
502
+ : ''}${result.imageSummary.missingSourceFiles
503
+ ? ` (${result.imageSummary.missingSourceFiles} missing local files)`
504
+ : ''}${result.imageSummary.storageFailures
505
+ ? ` (${result.imageSummary.storageFailures} storage failures)`
506
+ : ''}${result.imageSummary.metaFailures
507
+ ? ` (${result.imageSummary.metaFailures} metadata failures)`
508
+ : ''}`}
509
+ </react_native_1.Text>
510
+ </react_native_1.View>) : null}
511
+
512
+ {/* Per-entity breakdown */}
513
+ <react_native_1.View style={styles.entityList}>
514
+ {result.entities.map(e => (<EntityRow key={e.entity} entity={e.entity} uploaded={e.uploaded} skipped={e.skipped} conflicts={e.conflicts} failed={e.failed} errors={e.errors} conflictDetails={e.conflictDetails} firstError={e.firstError} showDevelopmentControls={showDevelopmentControls}/>))}
515
+ </react_native_1.View>
516
+
517
+ {/* Timestamp */}
518
+ <react_native_1.Text style={styles.resultTimestamp}>
519
+ {new Date(result.completedAt).toLocaleTimeString()}
520
+ </react_native_1.Text>
521
+
522
+ {/* Dismiss */}
523
+ <react_native_1.Pressable onPress={onDismissResult} accessibilityRole="button" accessibilityLabel="Dismiss sync result" accessibilityHint="Hides this sync result" style={({ pressed }) => [
524
+ styles.dismissResultButton,
525
+ pressed ? { opacity: 0.7 } : null,
526
+ ]}>
527
+ <vector_icons_1.MaterialIcons name="close" size={18} color={theme_1.COLORS.textMuted}/>
528
+ <react_native_1.Text style={styles.dismissResultText}>Dismiss</react_native_1.Text>
529
+ </react_native_1.Pressable>
530
+ </react_native_1.View>) : null}
531
+ </react_native_1.View>);
532
+ }
533
+ // ─────────────────────────────────────────────────────────────────────────────
534
+ // Styles
535
+ // ─────────────────────────────────────────────────────────────────────────────
536
+ const styles = react_native_1.StyleSheet.create({
537
+ root: { gap: theme_1.SPACING.sm },
538
+ // Scope note
539
+ scopeNote: {
540
+ fontSize: theme_1.FONTS.xs,
541
+ color: theme_1.COLORS.textMuted,
542
+ lineHeight: 18,
543
+ marginBottom: theme_1.SPACING.xs,
544
+ },
545
+ // Guest notice (signed-out)
546
+ guestNotice: {
547
+ flexDirection: 'row',
548
+ alignItems: 'flex-start',
549
+ gap: theme_1.SPACING.sm,
550
+ backgroundColor: theme_1.COLORS.surfaceHigh,
551
+ borderRadius: theme_1.RADIUS.md,
552
+ borderWidth: 1,
553
+ borderColor: theme_1.COLORS.border,
554
+ padding: theme_1.SPACING.md,
555
+ },
556
+ guestNoticeText: {
557
+ flex: 1,
558
+ fontSize: theme_1.FONTS.sm,
559
+ color: theme_1.COLORS.textMuted,
560
+ lineHeight: 20,
561
+ },
562
+ // Status row
563
+ statusRow: {
564
+ flexDirection: 'row',
565
+ alignItems: 'center',
566
+ gap: theme_1.SPACING.sm,
567
+ flexWrap: 'wrap',
568
+ },
569
+ badge: {
570
+ flexDirection: 'row',
571
+ alignItems: 'center',
572
+ gap: 4,
573
+ borderRadius: 100,
574
+ borderWidth: 1,
575
+ paddingHorizontal: theme_1.SPACING.sm,
576
+ paddingVertical: 4,
577
+ },
578
+ badgeCount: {
579
+ fontSize: theme_1.FONTS.sm,
580
+ fontWeight: '700',
581
+ lineHeight: 18,
582
+ },
583
+ badgeLabel: {
584
+ fontSize: theme_1.FONTS.xs,
585
+ fontWeight: '500',
586
+ },
587
+ offlineTag: {
588
+ flexDirection: 'row',
589
+ alignItems: 'center',
590
+ gap: 3,
591
+ borderRadius: 100,
592
+ borderWidth: 1,
593
+ borderColor: theme_1.COLORS.warning + '55',
594
+ backgroundColor: theme_1.COLORS.warning + '12',
595
+ paddingHorizontal: theme_1.SPACING.sm,
596
+ paddingVertical: 4,
597
+ },
598
+ offlineTagText: {
599
+ fontSize: theme_1.FONTS.xs,
600
+ color: theme_1.COLORS.textMuted,
601
+ fontWeight: '500',
602
+ },
603
+ refreshBtn: {
604
+ width: 44,
605
+ height: 44,
606
+ alignItems: 'center',
607
+ justifyContent: 'center',
608
+ marginLeft: 'auto',
609
+ borderRadius: theme_1.RADIUS.sm,
610
+ },
611
+ // Main sync button
612
+ uploadBtn: {
613
+ flexDirection: 'row',
614
+ alignItems: 'center',
615
+ justifyContent: 'center',
616
+ gap: theme_1.SPACING.sm,
617
+ backgroundColor: theme_1.COLORS.neonGreen + '18',
618
+ borderRadius: theme_1.RADIUS.lg,
619
+ borderWidth: 1.5,
620
+ borderColor: theme_1.COLORS.neonGreen + '55',
621
+ paddingVertical: theme_1.SPACING.md,
622
+ minHeight: 52,
623
+ },
624
+ uploadBtnActive: {
625
+ opacity: 1,
626
+ },
627
+ uploadBtnDisabled: {
628
+ opacity: 0.45,
629
+ },
630
+ uploadBtnWarning: {
631
+ backgroundColor: theme_1.COLORS.warning + '12',
632
+ borderColor: theme_1.COLORS.warning + '55',
633
+ },
634
+ uploadBtnText: {
635
+ fontSize: theme_1.FONTS.md,
636
+ fontWeight: '700',
637
+ color: theme_1.COLORS.neonGreen,
638
+ letterSpacing: 0.2,
639
+ },
640
+ uploadBtnTextDisabled: {
641
+ color: theme_1.COLORS.textMuted,
642
+ },
643
+ uploadBtnTextWarning: {
644
+ color: theme_1.COLORS.warning,
645
+ },
646
+ // Force sync (dev)
647
+ forceBtn: {
648
+ flexDirection: 'row',
649
+ alignItems: 'center',
650
+ justifyContent: 'center',
651
+ gap: theme_1.SPACING.xs,
652
+ paddingVertical: theme_1.SPACING.xs,
653
+ minHeight: 36,
654
+ },
655
+ forceBtnText: {
656
+ fontSize: theme_1.FONTS.xs,
657
+ color: theme_1.COLORS.textMuted,
658
+ fontStyle: 'italic',
659
+ },
660
+ // Guest adoption
661
+ guestAdoptSection: {
662
+ gap: theme_1.SPACING.sm,
663
+ },
664
+ dividerRow: {
665
+ flexDirection: 'row',
666
+ alignItems: 'center',
667
+ gap: theme_1.SPACING.sm,
668
+ marginVertical: theme_1.SPACING.xs,
669
+ },
670
+ dividerLine: {
671
+ flex: 1,
672
+ height: 1,
673
+ backgroundColor: theme_1.COLORS.border,
674
+ },
675
+ dividerLabel: {
676
+ fontSize: theme_1.FONTS.xs,
677
+ fontWeight: '700',
678
+ color: theme_1.COLORS.textMuted,
679
+ letterSpacing: 1.2,
680
+ },
681
+ guestAdoptCard: {
682
+ backgroundColor: theme_1.COLORS.neonCyan + '0A',
683
+ borderRadius: theme_1.RADIUS.lg,
684
+ borderWidth: 1,
685
+ borderColor: theme_1.COLORS.neonCyan + '33',
686
+ padding: theme_1.SPACING.md,
687
+ gap: theme_1.SPACING.sm,
688
+ },
689
+ guestAdoptHeader: {
690
+ flexDirection: 'row',
691
+ alignItems: 'center',
692
+ gap: theme_1.SPACING.xs,
693
+ },
694
+ guestAdoptTitle: {
695
+ fontSize: theme_1.FONTS.sm,
696
+ fontWeight: '700',
697
+ color: theme_1.COLORS.neonCyan,
698
+ letterSpacing: 0.3,
699
+ },
700
+ guestAdoptSubtitle: {
701
+ fontSize: theme_1.FONTS.xs,
702
+ color: theme_1.COLORS.textSecondary,
703
+ lineHeight: 18,
704
+ },
705
+ guestAdoptBtn: {
706
+ flexDirection: 'row',
707
+ alignItems: 'center',
708
+ justifyContent: 'center',
709
+ gap: theme_1.SPACING.sm,
710
+ backgroundColor: theme_1.COLORS.neonCyan + '18',
711
+ borderRadius: theme_1.RADIUS.lg,
712
+ borderWidth: 1.5,
713
+ borderColor: theme_1.COLORS.neonCyan + '55',
714
+ paddingVertical: theme_1.SPACING.md,
715
+ minHeight: 52,
716
+ },
717
+ guestAdoptBtnText: {
718
+ fontSize: theme_1.FONTS.md,
719
+ fontWeight: '700',
720
+ color: theme_1.COLORS.neonCyan,
721
+ letterSpacing: 0.2,
722
+ },
723
+ guestAdoptNote: {
724
+ flexDirection: 'row',
725
+ alignItems: 'flex-start',
726
+ gap: theme_1.SPACING.xs,
727
+ },
728
+ guestAdoptNoteText: {
729
+ flex: 1,
730
+ fontSize: theme_1.FONTS.xs,
731
+ color: theme_1.COLORS.textMuted,
732
+ lineHeight: 18,
733
+ },
734
+ // Result card
735
+ resultCard: {
736
+ borderRadius: theme_1.RADIUS.lg,
737
+ borderWidth: 1,
738
+ padding: theme_1.SPACING.md,
739
+ gap: theme_1.SPACING.xs,
740
+ },
741
+ resultCardSuccess: {
742
+ backgroundColor: theme_1.COLORS.neonGreen + '0C',
743
+ borderColor: theme_1.COLORS.neonGreen + '44',
744
+ },
745
+ resultCardWarning: {
746
+ backgroundColor: theme_1.COLORS.warning + '0C',
747
+ borderColor: theme_1.COLORS.warning + '44',
748
+ },
749
+ resultCardError: {
750
+ backgroundColor: theme_1.COLORS.error + '0C',
751
+ borderColor: theme_1.COLORS.error + '44',
752
+ },
753
+ resultHeader: {
754
+ flexDirection: 'row',
755
+ alignItems: 'flex-start',
756
+ gap: theme_1.SPACING.sm,
757
+ },
758
+ resultTitle: {
759
+ flex: 1,
760
+ fontSize: theme_1.FONTS.sm,
761
+ fontWeight: '700',
762
+ lineHeight: 20,
763
+ },
764
+ rpcErrorBox: {
765
+ flexDirection: 'row',
766
+ alignItems: 'flex-start',
767
+ gap: theme_1.SPACING.xs,
768
+ backgroundColor: theme_1.COLORS.error + '12',
769
+ borderRadius: theme_1.RADIUS.sm,
770
+ padding: theme_1.SPACING.sm,
771
+ marginTop: 2,
772
+ },
773
+ rpcErrorText: {
774
+ flex: 1,
775
+ fontSize: theme_1.FONTS.xs,
776
+ color: theme_1.COLORS.error,
777
+ lineHeight: 18,
778
+ fontFamily: 'monospace',
779
+ },
780
+ resultLegend: {
781
+ fontSize: theme_1.FONTS.xs,
782
+ color: theme_1.COLORS.textMuted,
783
+ lineHeight: 18,
784
+ },
785
+ entityList: {
786
+ gap: 2,
787
+ marginTop: theme_1.SPACING.xs,
788
+ },
789
+ entityRow: {
790
+ borderRadius: theme_1.RADIUS.sm,
791
+ overflow: 'hidden',
792
+ },
793
+ entityRowHeader: {
794
+ flexDirection: 'row',
795
+ alignItems: 'center',
796
+ gap: theme_1.SPACING.sm,
797
+ paddingVertical: 4,
798
+ paddingHorizontal: theme_1.SPACING.xs,
799
+ },
800
+ entityName: {
801
+ flex: 1,
802
+ fontSize: theme_1.FONTS.xs,
803
+ color: theme_1.COLORS.textSecondary,
804
+ fontWeight: '500',
805
+ textTransform: 'capitalize',
806
+ },
807
+ entityCounts: {
808
+ flexDirection: 'row',
809
+ gap: theme_1.SPACING.xs,
810
+ },
811
+ entityCount: {
812
+ fontSize: theme_1.FONTS.xs,
813
+ fontWeight: '700',
814
+ },
815
+ entityDetails: {
816
+ backgroundColor: theme_1.COLORS.surfaceHigh,
817
+ borderRadius: theme_1.RADIUS.sm,
818
+ padding: theme_1.SPACING.sm,
819
+ gap: 4,
820
+ marginBottom: theme_1.SPACING.xs,
821
+ },
822
+ detailRow: {
823
+ flexDirection: 'row',
824
+ alignItems: 'flex-start',
825
+ gap: theme_1.SPACING.xs,
826
+ },
827
+ detailText: {
828
+ flex: 1,
829
+ fontSize: theme_1.FONTS.xs,
830
+ color: theme_1.COLORS.textSecondary,
831
+ lineHeight: 18,
832
+ },
833
+ detailId: {
834
+ fontWeight: '700',
835
+ color: theme_1.COLORS.textMuted,
836
+ },
837
+ conflictBlock: {
838
+ backgroundColor: theme_1.COLORS.warning + '0A',
839
+ borderLeftWidth: 2,
840
+ borderLeftColor: theme_1.COLORS.warning + '88',
841
+ borderRadius: theme_1.RADIUS.sm,
842
+ paddingLeft: theme_1.SPACING.sm,
843
+ paddingVertical: theme_1.SPACING.xs,
844
+ gap: 3,
845
+ marginTop: 2,
846
+ marginBottom: 2,
847
+ },
848
+ conflictField: {
849
+ flexDirection: 'row',
850
+ gap: theme_1.SPACING.sm,
851
+ paddingLeft: 2,
852
+ },
853
+ conflictLabel: {
854
+ fontSize: theme_1.FONTS.xs,
855
+ color: theme_1.COLORS.textMuted,
856
+ fontFamily: 'monospace',
857
+ fontWeight: '600',
858
+ minWidth: 76,
859
+ lineHeight: 18,
860
+ },
861
+ conflictValue: {
862
+ flex: 1,
863
+ fontSize: theme_1.FONTS.xs,
864
+ color: theme_1.COLORS.textSecondary,
865
+ fontFamily: 'monospace',
866
+ lineHeight: 18,
867
+ },
868
+ rawToggle: {
869
+ flexDirection: 'row',
870
+ alignItems: 'center',
871
+ gap: 3,
872
+ paddingLeft: 2,
873
+ marginTop: 2,
874
+ },
875
+ rawToggleLabel: {
876
+ fontSize: theme_1.FONTS.xs,
877
+ color: theme_1.COLORS.textMuted,
878
+ fontStyle: 'italic',
879
+ },
880
+ rawJson: {
881
+ fontSize: 10,
882
+ color: theme_1.COLORS.textMuted,
883
+ fontFamily: 'monospace',
884
+ lineHeight: 16,
885
+ marginTop: 2,
886
+ },
887
+ imageSyncRow: {
888
+ flexDirection: 'row',
889
+ alignItems: 'center',
890
+ gap: 5,
891
+ marginTop: 1,
892
+ },
893
+ resultTimestamp: {
894
+ fontSize: theme_1.FONTS.xs,
895
+ color: theme_1.COLORS.textMuted,
896
+ textAlign: 'right',
897
+ marginTop: 2,
898
+ },
899
+ dismissResultButton: {
900
+ flexDirection: 'row',
901
+ alignItems: 'center',
902
+ justifyContent: 'center',
903
+ gap: theme_1.SPACING.xs,
904
+ paddingVertical: theme_1.SPACING.sm,
905
+ marginTop: theme_1.SPACING.sm,
906
+ },
907
+ dismissResultText: {
908
+ color: theme_1.COLORS.textMuted,
909
+ fontSize: 14,
910
+ fontWeight: '600',
911
+ },
912
+ });
913
+ //# sourceMappingURL=EchoSyncPanel.js.map