plant-health-lib 1.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 (112) hide show
  1. package/README.md +167 -0
  2. package/lib/components/index.d.ts +2 -0
  3. package/lib/components/index.d.ts.map +1 -0
  4. package/lib/components/index.js +19 -0
  5. package/lib/components/index.js.map +1 -0
  6. package/lib/components/ui.d.ts +33 -0
  7. package/lib/components/ui.d.ts.map +1 -0
  8. package/lib/components/ui.js +184 -0
  9. package/lib/components/ui.js.map +1 -0
  10. package/lib/contexts/HistoryContext.d.ts +30 -0
  11. package/lib/contexts/HistoryContext.d.ts.map +1 -0
  12. package/lib/contexts/HistoryContext.js +76 -0
  13. package/lib/contexts/HistoryContext.js.map +1 -0
  14. package/lib/hooks/index.d.ts +3 -0
  15. package/lib/hooks/index.d.ts.map +1 -0
  16. package/lib/hooks/index.js +9 -0
  17. package/lib/hooks/index.js.map +1 -0
  18. package/lib/hooks/useFrameClassifier.d.ts +9 -0
  19. package/lib/hooks/useFrameClassifier.d.ts.map +1 -0
  20. package/lib/hooks/useFrameClassifier.js +72 -0
  21. package/lib/hooks/useFrameClassifier.js.map +1 -0
  22. package/lib/hooks/useHistory.d.ts +3 -0
  23. package/lib/hooks/useHistory.d.ts.map +1 -0
  24. package/lib/hooks/useHistory.js +6 -0
  25. package/lib/hooks/useHistory.js.map +1 -0
  26. package/lib/index.d.ts +8 -0
  27. package/lib/index.d.ts.map +1 -0
  28. package/lib/index.js +25 -0
  29. package/lib/index.js.map +1 -0
  30. package/lib/navigation/RootNavigator.d.ts +3 -0
  31. package/lib/navigation/RootNavigator.d.ts.map +1 -0
  32. package/lib/navigation/RootNavigator.js +36 -0
  33. package/lib/navigation/RootNavigator.js.map +1 -0
  34. package/lib/screens/HistoryScreen.d.ts +3 -0
  35. package/lib/screens/HistoryScreen.d.ts.map +1 -0
  36. package/lib/screens/HistoryScreen.js +93 -0
  37. package/lib/screens/HistoryScreen.js.map +1 -0
  38. package/lib/screens/HomeScreen.d.ts +3 -0
  39. package/lib/screens/HomeScreen.d.ts.map +1 -0
  40. package/lib/screens/HomeScreen.js +121 -0
  41. package/lib/screens/HomeScreen.js.map +1 -0
  42. package/lib/screens/ResultScreen.d.ts +3 -0
  43. package/lib/screens/ResultScreen.d.ts.map +1 -0
  44. package/lib/screens/ResultScreen.js +166 -0
  45. package/lib/screens/ResultScreen.js.map +1 -0
  46. package/lib/screens/ScanScreen.d.ts +3 -0
  47. package/lib/screens/ScanScreen.d.ts.map +1 -0
  48. package/lib/screens/ScanScreen.js +311 -0
  49. package/lib/screens/ScanScreen.js.map +1 -0
  50. package/lib/services/classifier.d.ts +27 -0
  51. package/lib/services/classifier.d.ts.map +1 -0
  52. package/lib/services/classifier.js +70 -0
  53. package/lib/services/classifier.js.map +1 -0
  54. package/lib/services/diseaseData.d.ts +19 -0
  55. package/lib/services/diseaseData.d.ts.map +1 -0
  56. package/lib/services/diseaseData.js +180 -0
  57. package/lib/services/diseaseData.js.map +1 -0
  58. package/lib/services/index.d.ts +3 -0
  59. package/lib/services/index.d.ts.map +1 -0
  60. package/lib/services/index.js +20 -0
  61. package/lib/services/index.js.map +1 -0
  62. package/lib/theme/index.d.ts +37 -0
  63. package/lib/theme/index.d.ts.map +1 -0
  64. package/lib/theme/index.js +52 -0
  65. package/lib/theme/index.js.map +1 -0
  66. package/lib/utils/imageToTensor.d.ts +2 -0
  67. package/lib/utils/imageToTensor.d.ts.map +1 -0
  68. package/lib/utils/imageToTensor.js +36 -0
  69. package/lib/utils/imageToTensor.js.map +1 -0
  70. package/lib/utils/index.d.ts +2 -0
  71. package/lib/utils/index.d.ts.map +1 -0
  72. package/lib/utils/index.js +19 -0
  73. package/lib/utils/index.js.map +1 -0
  74. package/package.json +104 -0
  75. package/src/assets/README.md +24 -0
  76. package/src/assets/plant_disease_model.tflite +0 -0
  77. package/src/components/index.js +9 -0
  78. package/src/components/index.ts +2 -0
  79. package/src/components/ui.js +205 -0
  80. package/src/components/ui.tsx +215 -0
  81. package/src/contexts/HistoryContext.js +130 -0
  82. package/src/contexts/HistoryContext.tsx +84 -0
  83. package/src/hooks/index.js +8 -0
  84. package/src/hooks/index.ts +3 -0
  85. package/src/hooks/useFrameClassifier.js +120 -0
  86. package/src/hooks/useFrameClassifier.ts +80 -0
  87. package/src/hooks/useHistory.js +5 -0
  88. package/src/hooks/useHistory.ts +2 -0
  89. package/src/index.js +24 -0
  90. package/src/index.ts +12 -0
  91. package/src/navigation/RootNavigator.js +43 -0
  92. package/src/navigation/RootNavigator.tsx +39 -0
  93. package/src/screens/HistoryScreen.js +98 -0
  94. package/src/screens/HistoryScreen.tsx +100 -0
  95. package/src/screens/HomeScreen.js +125 -0
  96. package/src/screens/HomeScreen.tsx +137 -0
  97. package/src/screens/ResultScreen.js +166 -0
  98. package/src/screens/ResultScreen.tsx +157 -0
  99. package/src/screens/ScanScreen.js +388 -0
  100. package/src/screens/ScanScreen.tsx +341 -0
  101. package/src/services/classifier.js +122 -0
  102. package/src/services/classifier.ts +83 -0
  103. package/src/services/diseaseData.js +191 -0
  104. package/src/services/diseaseData.ts +192 -0
  105. package/src/services/index.js +19 -0
  106. package/src/services/index.ts +3 -0
  107. package/src/theme/index.js +51 -0
  108. package/src/theme/index.ts +51 -0
  109. package/src/utils/imageToTensor.js +80 -0
  110. package/src/utils/imageToTensor.ts +36 -0
  111. package/src/utils/index.js +18 -0
  112. package/src/utils/index.ts +2 -0
package/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # 🌿 plant-health-lib
2
+
3
+ A comprehensive React Native library for on-device plant disease detection. Point the camera at a leaf (or upload a photo) and a quantized TFLite model classifies 38+ crop diseases — fully offline, no backend.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install plant-health-lib
9
+ # or
10
+ yarn add plant-health-lib
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { useFrameClassifier, useHistory } from 'plant-health-lib/hooks';
17
+ import { getDiseaseInfo } from 'plant-health-lib/services';
18
+ import { classifyImage } from 'plant-health-lib/utils';
19
+ ```
20
+
21
+ ## Features
22
+
23
+ - ✅ **38+ Disease Classes** – Comprehensive crop disease database
24
+ - ✅ **TensorFlow Lite Integration** – Fast GPU-accelerated inference
25
+ - ✅ **Live Camera Detection** – Real-time classification from camera frames
26
+ - ✅ **Offline Operation** – No backend required, fully on-device
27
+ - ✅ **TypeScript Support** – Full type safety included
28
+ - ✅ **History Tracking** – AsyncStorage-backed scan history
29
+
30
+ ## Stack
31
+
32
+ | Concern | Library |
33
+ |---|---|
34
+ | AI inference | `react-native-fast-tflite` (GPU/CoreML delegates) |
35
+ | Camera + live frames | `react-native-vision-camera` + `vision-camera-resize-plugin` |
36
+ | Frame-processor threading | `react-native-worklets-core`, `react-native-reanimated` |
37
+ | Navigation | `@react-navigation/native-stack` |
38
+ | Persistence | `@react-native-async-storage/async-storage` |
39
+ | Gallery upload | `react-native-image-picker` |
40
+
41
+ ## Architecture
42
+
43
+ ```
44
+ App.tsx warms up the model on launch
45
+ src/
46
+ navigation/RootNavigator Home → Scan → Result, + History
47
+ screens/
48
+ HomeScreen dashboard, recent scans, entry points
49
+ ScanScreen live camera w/ throttled frame classification + capture/upload
50
+ ResultScreen diagnosis, confidence, treatment, prevention, top-K
51
+ HistoryScreen saved scans
52
+ services/
53
+ classifier.ts loads .tflite, normalizes input, runs inference, softmax + top-K
54
+ diseaseData.ts 38-class label map + remedy/prevention knowledge base
55
+ hooks/
56
+ useFrameClassifier.ts vision-camera frame processor → resize (GPU) → TFLite on worklet
57
+ useHistory.ts AsyncStorage-backed scan history
58
+ utils/imageToTensor.ts still-image decode + resize to 224×224×3
59
+ components/ui.tsx Card, Button, SeverityBadge, ConfidenceBar
60
+ theme/ design tokens
61
+ ```
62
+
63
+ **Two inference paths:** live camera frames run through the GPU resize plugin inside a worklet (fast, ~1 fps throttled); still captures/uploads go through `imageToTensor`. Both feed the same `classifier.classify()`.
64
+
65
+ ## Usage
66
+
67
+ ### Using the Frame Classifier Hook (Live Camera)
68
+
69
+ ```typescript
70
+ import { useFrameClassifier } from 'plant-health-lib/hooks';
71
+ import { getDiseaseInfo } from 'plant-health-lib/services';
72
+
73
+ export function ScanComponent() {
74
+ const { classify, isLoading, lastResult } = useFrameClassifier();
75
+
76
+ useEffect(() => {
77
+ if (lastResult) {
78
+ const disease = getDiseaseInfo(lastResult.label);
79
+ console.log(`Detected: ${disease.name} (${lastResult.confidence}%)`);
80
+ }
81
+ }, [lastResult]);
82
+
83
+ return (
84
+ <CameraView
85
+ onFrame={(frame) => classify(frame)}
86
+ // ... camera props
87
+ />
88
+ );
89
+ }
90
+ ```
91
+
92
+ ### Using the History Hook
93
+
94
+ ```typescript
95
+ import { useHistory } from 'plant-health-lib/hooks';
96
+
97
+ const { scans, addScan, clearHistory } = useHistory();
98
+ ```
99
+
100
+ ### Classifying Images
101
+
102
+ ```typescript
103
+ import { classifyImage } from 'plant-health-lib/utils';
104
+ import { classifier } from 'plant-health-lib/services';
105
+
106
+ const result = await classifyImage(imagePath);
107
+ // result: { label: string; confidence: number; }
108
+ ```
109
+
110
+ ## API Reference
111
+
112
+ ### Hooks
113
+
114
+ - **`useFrameClassifier()`** – Real-time frame classification from camera
115
+ - Returns: `{ classify(frame), isLoading, lastResult }`
116
+
117
+ - **`useHistory()`** – Manage scan history with AsyncStorage
118
+ - Returns: `{ scans, addScan(result), clearHistory() }`
119
+
120
+ ### Services
121
+
122
+ - **`classifier.classify(tensor)`** – Classify a TensorFlow tensor
123
+ - **`getDiseaseInfo(label)`** – Get remedy/prevention data for a disease
124
+
125
+ ### Utils
126
+
127
+ - **`classifyImage(path)`** – Load and classify a still image from file path
128
+
129
+ ### Components
130
+
131
+ - **`<UI.Card />`** – Styled card component
132
+ - **`<UI.Button />`** – Button component
133
+ - **`<UI.SeverityBadge />`** – Disease severity indicator
134
+ - **`<UI.ConfidenceBar />`** – Confidence visualization
135
+
136
+ ## Setting Up the Model
137
+
138
+ The library expects `src/assets/plant_disease_model.tflite`:
139
+ - **Input:** `[1, 224, 224, 3]` float32
140
+ - **Output:** `[1, 38]` (logits or probabilities)
141
+ - **Labels:** Must match `LABELS` in `src/services/diseaseData.ts`
142
+
143
+ Train on PlantVillage dataset using MobileNetV2 or EfficientNet-Lite, then convert with TFLite converter. See `src/assets/README.md` for details.
144
+
145
+ ## Building from Source
146
+
147
+ ```bash
148
+ npm install
149
+ npm run build
150
+ ```
151
+
152
+ The compiled library will be in `lib/`.
153
+
154
+ ## Contributing
155
+
156
+ We welcome contributions! Please feel free to submit a Pull Request.
157
+
158
+ ## License
159
+
160
+ MIT
161
+
162
+ ## Notes / Production Hardening
163
+
164
+ - The remedy text is general guidance — maintain an on-screen disclaimer; it's not a substitute for professional agricultural advice.
165
+ - For production, replace the JS image resampler with a native bitmap decoder for better performance.
166
+ - Tune `MEAN`/`STD` in `classifier.ts` to match your training normalization.
167
+
@@ -0,0 +1,2 @@
1
+ export * from './ui';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AACA,cAAc,MAAM,CAAC"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // Export all components
18
+ __exportStar(require("./ui"), exports);
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wBAAwB;AACxB,uCAAqB"}
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { ViewStyle } from 'react-native';
3
+ import type { Severity } from '../services/diseaseData';
4
+ export declare const TrashIcon: React.FC<{
5
+ size?: number;
6
+ color?: string;
7
+ }>;
8
+ export declare const Card: React.FC<{
9
+ children: React.ReactNode;
10
+ style?: ViewStyle;
11
+ }>;
12
+ export declare const Button: React.FC<{
13
+ label: string;
14
+ onPress: () => void;
15
+ variant?: 'primary' | 'ghost' | 'danger';
16
+ loading?: boolean;
17
+ icon?: React.ReactNode;
18
+ }>;
19
+ export declare const SeverityBadge: React.FC<{
20
+ severity: Severity;
21
+ }>;
22
+ export declare const ConfidenceBar: React.FC<{
23
+ value: number;
24
+ }>;
25
+ export declare const ConfirmSheet: React.FC<{
26
+ visible: boolean;
27
+ title: string;
28
+ message?: string;
29
+ confirmLabel?: string;
30
+ onConfirm: () => void;
31
+ onClose: () => void;
32
+ }>;
33
+ //# sourceMappingURL=ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/components/ui.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAC3D,OAAO,EAKL,SAAS,EAIV,MAAM,cAAc,CAAC;AAGtB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAExD,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAgBjE,CAAC;AAEF,eAAO,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,CAGlB,CAAC;AAE3D,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB,CA4BA,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAO1D,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CASrD,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAwCA,CAAC"}
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.ConfirmSheet = exports.ConfidenceBar = exports.SeverityBadge = exports.Button = exports.Card = exports.TrashIcon = void 0;
27
+ const react_1 = __importStar(require("react"));
28
+ const react_native_1 = require("react-native");
29
+ const react_native_svg_1 = __importStar(require("react-native-svg"));
30
+ const theme_1 = require("../theme");
31
+ const TrashIcon = ({ size = 22, color = theme_1.colors.danger, }) => (<react_native_svg_1.default width={size} height={size} viewBox="0 0 22 20" fill="none">
32
+ <react_native_svg_1.Path d="M3 6h18" stroke={color} strokeWidth={1.8} strokeLinecap="round"/>
33
+ <react_native_svg_1.Path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2" stroke={color} strokeWidth={1.8} strokeLinecap="round"/>
34
+ <react_native_svg_1.Rect x="5" y="6" width="14" height="14" rx="2" stroke={color} strokeWidth={1.8}/>
35
+ <react_native_svg_1.Line x1="10" y1="11" x2="10" y2="17" stroke={color} strokeWidth={1.8} strokeLinecap="round"/>
36
+ <react_native_svg_1.Line x1="14" y1="11" x2="14" y2="17" stroke={color} strokeWidth={1.8} strokeLinecap="round"/>
37
+ </react_native_svg_1.default>);
38
+ exports.TrashIcon = TrashIcon;
39
+ const Card = ({ children, style, }) => <react_native_1.View style={[styles.card, style]}>{children}</react_native_1.View>;
40
+ exports.Card = Card;
41
+ const Button = ({ label, onPress, variant = 'primary', loading, icon }) => {
42
+ const bg = variant === 'primary' ? theme_1.colors.primary : variant === 'danger' ? theme_1.colors.danger : 'transparent';
43
+ const fg = variant === 'ghost' ? theme_1.colors.text : '#08130C';
44
+ return (<react_native_1.TouchableOpacity activeOpacity={0.85} onPress={onPress} disabled={loading} style={[
45
+ styles.btn,
46
+ {
47
+ backgroundColor: bg,
48
+ borderWidth: variant === 'ghost' ? 1 : 0,
49
+ borderColor: theme_1.colors.border,
50
+ },
51
+ ]}>
52
+ {loading ? (<react_native_1.ActivityIndicator color={fg}/>) : (<react_native_1.View style={styles.btnRow}>
53
+ {icon}
54
+ <react_native_1.Text style={[styles.btnText, { color: fg }]}>{label}</react_native_1.Text>
55
+ </react_native_1.View>)}
56
+ </react_native_1.TouchableOpacity>);
57
+ };
58
+ exports.Button = Button;
59
+ const SeverityBadge = ({ severity }) => (<react_native_1.View style={[styles.badge, { backgroundColor: (0, theme_1.severityColor)(severity) + '22' }]}>
60
+ <react_native_1.View style={[styles.dot, { backgroundColor: (0, theme_1.severityColor)(severity) }]}/>
61
+ <react_native_1.Text style={[styles.badgeText, { color: (0, theme_1.severityColor)(severity) }]}>
62
+ {severity.toUpperCase()}
63
+ </react_native_1.Text>
64
+ </react_native_1.View>);
65
+ exports.SeverityBadge = SeverityBadge;
66
+ const ConfidenceBar = ({ value }) => (<react_native_1.View style={styles.barTrack}>
67
+ <react_native_1.View style={[
68
+ styles.barFill,
69
+ { width: `${Math.round(value * 100)}%`, backgroundColor: theme_1.colors.primary },
70
+ ]}/>
71
+ </react_native_1.View>);
72
+ exports.ConfidenceBar = ConfidenceBar;
73
+ const ConfirmSheet = ({ visible, title, message, confirmLabel = 'Delete', onConfirm, onClose }) => {
74
+ const [open, setOpen] = (0, react_1.useState)(false);
75
+ const translateY = (0, react_1.useRef)(new react_native_1.Animated.Value(300)).current;
76
+ const backdropOpacity = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
77
+ (0, react_1.useEffect)(() => {
78
+ if (visible) {
79
+ setOpen(true);
80
+ react_native_1.Animated.parallel([
81
+ react_native_1.Animated.spring(translateY, { toValue: 0, useNativeDriver: true, bounciness: 4 }),
82
+ react_native_1.Animated.timing(backdropOpacity, { toValue: 1, duration: 200, useNativeDriver: true }),
83
+ ]).start();
84
+ }
85
+ else {
86
+ react_native_1.Animated.parallel([
87
+ react_native_1.Animated.timing(translateY, { toValue: 300, duration: 200, useNativeDriver: true }),
88
+ react_native_1.Animated.timing(backdropOpacity, { toValue: 0, duration: 200, useNativeDriver: true }),
89
+ ]).start(() => setOpen(false));
90
+ }
91
+ }, [visible]);
92
+ return (<react_native_1.Modal visible={open} transparent animationType="none" onRequestClose={onClose}>
93
+ <react_native_1.View style={react_native_1.StyleSheet.absoluteFill}>
94
+ <react_native_1.Animated.View style={[styles.sheetBackdrop, { opacity: backdropOpacity }]}>
95
+ <react_native_1.TouchableOpacity style={{ flex: 1 }} onPress={onClose} activeOpacity={1}/>
96
+ </react_native_1.Animated.View>
97
+ <react_native_1.View style={{ flex: 1, justifyContent: 'flex-end' }}>
98
+ <react_native_1.Animated.View style={[styles.sheet, { transform: [{ translateY }] }]}>
99
+ <react_native_1.View style={styles.handle}/>
100
+ <react_native_1.Text style={styles.sheetTitle}>{title}</react_native_1.Text>
101
+ {message ? <react_native_1.Text style={styles.sheetMessage}>{message}</react_native_1.Text> : null}
102
+ <react_native_1.View style={styles.sheetActions}>
103
+ <exports.Button label={confirmLabel} onPress={onConfirm} variant="danger"/>
104
+ <exports.Button label="Cancel" onPress={onClose} variant="ghost"/>
105
+ </react_native_1.View>
106
+ </react_native_1.Animated.View>
107
+ </react_native_1.View>
108
+ </react_native_1.View>
109
+ </react_native_1.Modal>);
110
+ };
111
+ exports.ConfirmSheet = ConfirmSheet;
112
+ const styles = react_native_1.StyleSheet.create({
113
+ card: {
114
+ backgroundColor: theme_1.colors.surface,
115
+ borderRadius: theme_1.radius.lg,
116
+ padding: theme_1.spacing.lg,
117
+ borderWidth: 1,
118
+ borderColor: theme_1.colors.border,
119
+ },
120
+ btn: {
121
+ borderRadius: theme_1.radius.pill,
122
+ paddingVertical: theme_1.spacing.md,
123
+ paddingHorizontal: theme_1.spacing.lg,
124
+ alignItems: 'center',
125
+ justifyContent: 'center',
126
+ },
127
+ btnRow: { flexDirection: 'row', alignItems: 'center', gap: theme_1.spacing.sm },
128
+ btnText: { fontSize: theme_1.font.body, fontWeight: '700' },
129
+ badge: {
130
+ flexDirection: 'row',
131
+ alignItems: 'center',
132
+ gap: theme_1.spacing.xs,
133
+ paddingHorizontal: theme_1.spacing.sm,
134
+ paddingVertical: 4,
135
+ borderRadius: theme_1.radius.pill,
136
+ alignSelf: 'flex-start',
137
+ },
138
+ dot: { width: 8, height: 8, borderRadius: 4 },
139
+ badgeText: { fontSize: theme_1.font.tiny, fontWeight: '800', letterSpacing: 0.5 },
140
+ barTrack: {
141
+ height: 8,
142
+ backgroundColor: theme_1.colors.surfaceAlt,
143
+ borderRadius: theme_1.radius.pill,
144
+ overflow: 'hidden',
145
+ },
146
+ barFill: { height: 8, borderRadius: theme_1.radius.pill },
147
+ sheetBackdrop: {
148
+ ...react_native_1.StyleSheet.absoluteFillObject,
149
+ backgroundColor: 'rgba(0,0,0,0.6)',
150
+ },
151
+ sheet: {
152
+ backgroundColor: theme_1.colors.surface,
153
+ borderTopLeftRadius: theme_1.radius.lg,
154
+ borderTopRightRadius: theme_1.radius.lg,
155
+ borderWidth: 1,
156
+ borderColor: theme_1.colors.border,
157
+ paddingHorizontal: theme_1.spacing.lg,
158
+ paddingBottom: theme_1.spacing.xl,
159
+ paddingTop: theme_1.spacing.md,
160
+ },
161
+ handle: {
162
+ width: 40,
163
+ height: 4,
164
+ borderRadius: theme_1.radius.pill,
165
+ backgroundColor: theme_1.colors.border,
166
+ alignSelf: 'center',
167
+ marginBottom: theme_1.spacing.lg,
168
+ },
169
+ sheetTitle: {
170
+ color: theme_1.colors.text,
171
+ fontSize: theme_1.font.h3,
172
+ fontWeight: '700',
173
+ textAlign: 'center',
174
+ marginBottom: theme_1.spacing.xs,
175
+ },
176
+ sheetMessage: {
177
+ color: theme_1.colors.textDim,
178
+ fontSize: theme_1.font.body,
179
+ textAlign: 'center',
180
+ marginBottom: theme_1.spacing.md,
181
+ },
182
+ sheetActions: { gap: theme_1.spacing.sm, marginTop: theme_1.spacing.lg },
183
+ });
184
+ //# sourceMappingURL=ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/components/ui.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import type { Prediction } from '../services/classifier';
3
+ export interface ScanRecord {
4
+ id: string;
5
+ uri: string;
6
+ label: string;
7
+ crop: string;
8
+ name: string;
9
+ healthy: boolean;
10
+ severity: string;
11
+ confidence: number;
12
+ topK: {
13
+ label: string;
14
+ confidence: number;
15
+ }[];
16
+ date: number;
17
+ }
18
+ interface HistoryContextValue {
19
+ records: ScanRecord[];
20
+ loaded: boolean;
21
+ add: (uri: string, p: Prediction) => ScanRecord;
22
+ remove: (id: string) => void;
23
+ clear: () => void;
24
+ }
25
+ export declare function HistoryProvider({ children }: {
26
+ children: React.ReactNode;
27
+ }): React.JSX.Element;
28
+ export declare function useHistoryContext(): HistoryContextValue;
29
+ export {};
30
+ //# sourceMappingURL=HistoryContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HistoryContext.d.ts","sourceRoot":"","sources":["../../src/contexts/HistoryContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsE,MAAM,OAAO,CAAC;AAE3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIzD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,mBAAmB;IAC3B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,KAAK,UAAU,CAAC;IAChD,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAID,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,qBAgD1E;AAED,wBAAgB,iBAAiB,IAAI,mBAAmB,CAIvD"}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.HistoryProvider = HistoryProvider;
30
+ exports.useHistoryContext = useHistoryContext;
31
+ const react_1 = __importStar(require("react"));
32
+ const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
33
+ const KEY = '@plantdoctor/history';
34
+ const HistoryContext = (0, react_1.createContext)(null);
35
+ function HistoryProvider({ children }) {
36
+ const [records, setRecords] = (0, react_1.useState)([]);
37
+ const [loaded, setLoaded] = (0, react_1.useState)(false);
38
+ (0, react_1.useEffect)(() => {
39
+ async_storage_1.default.getItem(KEY)
40
+ .then((raw) => setRecords(raw ? JSON.parse(raw) : []))
41
+ .catch(() => setRecords([]))
42
+ .finally(() => setLoaded(true));
43
+ }, []);
44
+ const persist = (0, react_1.useCallback)(async (next) => {
45
+ setRecords(next);
46
+ await async_storage_1.default.setItem(KEY, JSON.stringify(next));
47
+ }, []);
48
+ const add = (0, react_1.useCallback)((uri, p) => {
49
+ const rec = {
50
+ id: `${Date.now()}`,
51
+ uri,
52
+ label: p.info.label,
53
+ crop: p.info.crop,
54
+ name: p.info.name,
55
+ healthy: p.info.healthy,
56
+ severity: p.info.severity,
57
+ confidence: p.confidence,
58
+ topK: p.topK,
59
+ date: Date.now(),
60
+ };
61
+ persist([rec, ...records].slice(0, 100));
62
+ return rec;
63
+ }, [records, persist]);
64
+ const remove = (0, react_1.useCallback)((id) => persist(records.filter((r) => r.id !== id)), [records, persist]);
65
+ const clear = (0, react_1.useCallback)(() => persist([]), [persist]);
66
+ return (<HistoryContext.Provider value={{ records, loaded, add, remove, clear }}>
67
+ {children}
68
+ </HistoryContext.Provider>);
69
+ }
70
+ function useHistoryContext() {
71
+ const ctx = (0, react_1.useContext)(HistoryContext);
72
+ if (!ctx)
73
+ throw new Error('useHistoryContext must be used within HistoryProvider');
74
+ return ctx;
75
+ }
76
+ //# sourceMappingURL=HistoryContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HistoryContext.js","sourceRoot":"","sources":["../../src/contexts/HistoryContext.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,0CAgDC;AAED,8CAIC;AAnFD,+CAA2F;AAC3F,8FAAqE;AAGrE,MAAM,GAAG,GAAG,sBAAsB,CAAC;AAuBnC,MAAM,cAAc,GAAG,IAAA,qBAAa,EAA6B,IAAI,CAAC,CAAC;AAEvE,SAAgB,eAAe,CAAC,EAAE,QAAQ,EAAiC;IACzE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,IAAA,gBAAQ,EAAe,EAAE,CAAC,CAAC;IACzD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAA,gBAAQ,EAAC,KAAK,CAAC,CAAC;IAE5C,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,uBAAY,CAAC,OAAO,CAAC,GAAG,CAAC;aACtB,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;aACrD,KAAK,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;aAC3B,OAAO,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,OAAO,GAAG,IAAA,mBAAW,EAAC,KAAK,EAAE,IAAkB,EAAE,EAAE;QACvD,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,uBAAY,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IACxD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,GAAG,GAAG,IAAA,mBAAW,EACrB,CAAC,GAAW,EAAE,CAAa,EAAE,EAAE;QAC7B,MAAM,GAAG,GAAe;YACtB,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE;YACnB,GAAG;YACH,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK;YACnB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI;YACjB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI;YACjB,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO;YACvB,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ;YACzB,UAAU,EAAE,CAAC,CAAC,UAAU;YACxB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE;SACjB,CAAC;QACF,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACzC,OAAO,GAAG,CAAC;IACb,CAAC,EACD,CAAC,OAAO,EAAE,OAAO,CAAC,CACnB,CAAC;IAEF,MAAM,MAAM,GAAG,IAAA,mBAAW,EACxB,CAAC,EAAU,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,EAC3D,CAAC,OAAO,EAAE,OAAO,CAAC,CACnB,CAAC;IAEF,MAAM,KAAK,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAExD,OAAO,CACL,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CACtE;MAAA,CAAC,QAAQ,CACX;IAAA,EAAE,cAAc,CAAC,QAAQ,CAAC,CAC3B,CAAC;AACJ,CAAC;AAED,SAAgB,iBAAiB;IAC/B,MAAM,GAAG,GAAG,IAAA,kBAAU,EAAC,cAAc,CAAC,CAAC;IACvC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IACnF,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { useFrameClassifier } from './useFrameClassifier';
2
+ export { useHistory } from './useHistory';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useHistory = exports.useFrameClassifier = void 0;
4
+ // Export all hooks
5
+ var useFrameClassifier_1 = require("./useFrameClassifier");
6
+ Object.defineProperty(exports, "useFrameClassifier", { enumerable: true, get: function () { return useFrameClassifier_1.useFrameClassifier; } });
7
+ var useHistory_1 = require("./useHistory");
8
+ Object.defineProperty(exports, "useHistory", { enumerable: true, get: function () { return useHistory_1.useHistory; } });
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":";;;AAAA,mBAAmB;AACnB,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAC3B,2CAA0C;AAAjC,wGAAA,UAAU,OAAA"}
@@ -0,0 +1,9 @@
1
+ import { Prediction } from '../services/classifier';
2
+ export declare function useFrameClassifier(enabled: boolean, intervalMs?: number): {
3
+ frameProcessor: import("react-native-vision-camera").ReadonlyFrameProcessor;
4
+ prediction: Prediction | null;
5
+ running: boolean;
6
+ setRunning: import("react").Dispatch<import("react").SetStateAction<boolean>>;
7
+ isPlantDetected: boolean;
8
+ };
9
+ //# sourceMappingURL=useFrameClassifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFrameClassifier.d.ts","sourceRoot":"","sources":["../../src/hooks/useFrameClassifier.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgC,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAuBlF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,SAAO;;;;;;EAoDrE"}