Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 60 additions & 5 deletions apps/bare_rn/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
StyleSheet,
Text,
TextInput,
TouchableOpacity,
TouchableWithoutFeedback,
View,
} from 'react-native';
Expand Down Expand Up @@ -82,9 +83,64 @@ const spinnerStyles = StyleSheet.create({
},
});

function ErrorBanner({
message,
onDismiss,
}: {
message: string | null;
onDismiss: () => void;
}) {
if (!message) return null;
return (
<View style={errorBannerStyles.container}>
<Text style={errorBannerStyles.message} numberOfLines={3}>
{message}
</Text>
<TouchableOpacity
onPress={onDismiss}
style={errorBannerStyles.closeButton}
>
<Text style={errorBannerStyles.closeText}>✕</Text>
</TouchableOpacity>
</View>
);
}

const errorBannerStyles = StyleSheet.create({
container: {
backgroundColor: '#FEE2E2',
borderLeftWidth: 4,
borderLeftColor: '#EF4444',
borderRadius: 8,
marginHorizontal: 16,
marginVertical: 8,
paddingVertical: 10,
paddingLeft: 12,
paddingRight: 8,
flexDirection: 'row',
alignItems: 'center',
},
message: {
flex: 1,
color: '#991B1B',
fontSize: 14,
lineHeight: 20,
},
closeButton: {
padding: 4,
marginLeft: 8,
},
closeText: {
color: '#991B1B',
fontSize: 16,
fontWeight: '600',
},
});

function App() {
const [userInput, setUserInput] = useState('');
const [isTextInputFocused, setIsTextInputFocused] = useState(false);
const [error, setError] = useState<string | null>(null);
const textInputRef = useRef<TextInput>(null);
const scrollViewRef = useRef<ScrollView>(null);

Expand All @@ -97,9 +153,7 @@ function App() {
// } });

useEffect(() => {
if (llm.error) {
console.log('LLM error:', llm.error);
}
if (llm.error) setError(String(llm.error));
}, [llm.error]);

const sendMessage = async () => {
Expand All @@ -110,7 +164,7 @@ function App() {
try {
await llm.sendMessage(userInput);
} catch (e) {
console.error(e);
setError(e instanceof Error ? e.message : String(e));
}
};

Expand All @@ -122,11 +176,12 @@ function App() {
keyboardVerticalOffset={Platform.OS === 'ios' ? 100 : 0}
>
<Spinner
visible={!llm.isReady}
visible={!llm.isReady && !llm.error}
textContent={`Loading model ${(llm.downloadProgress * 100).toFixed(0)}%`}
/>

<SafeAreaView style={styles.content}>
<ErrorBanner message={error} onDismiss={() => setError(null)} />
{llm.messageHistory.length > 0 || llm.isGenerating ? (
<ScrollView
ref={scrollViewRef}
Expand Down
9 changes: 8 additions & 1 deletion apps/computer-vision/app/object_detection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import ImageWithBboxes from '../../components/ImageWithBboxes';
import React, { useContext, useEffect, useState } from 'react';
import { GeneratingContext } from '../../context';
import ScreenWrapper from '../../ScreenWrapper';
import ErrorBanner from '../../components/ErrorBanner';

export default function ObjectDetectionScreen() {
const [imageUri, setImageUri] = useState('');
const [results, setResults] = useState<Detection[]>([]);
const [error, setError] = useState<string | null>(null);
const [imageDimensions, setImageDimensions] = useState<{
width: number;
height: number;
Expand All @@ -26,6 +28,10 @@ export default function ObjectDetectionScreen() {
setGlobalGenerating(rfDetr.isGenerating);
}, [rfDetr.isGenerating, setGlobalGenerating]);

useEffect(() => {
if (rfDetr.error) setError(String(rfDetr.error));
}, [rfDetr.error]);

const handleCameraPress = async (isCamera: boolean) => {
const image = await getImage(isCamera);
const uri = image?.uri;
Expand All @@ -45,7 +51,7 @@ export default function ObjectDetectionScreen() {
const output = await rfDetr.forward(imageUri);
setResults(output);
} catch (e) {
console.error(e);
setError(e instanceof Error ? e.message : String(e));
}
}
};
Expand All @@ -61,6 +67,7 @@ export default function ObjectDetectionScreen() {

return (
<ScreenWrapper>
<ErrorBanner message={error} onDismiss={() => setError(null)} />
<View style={styles.imageContainer}>
<View style={styles.image}>
{imageUri && imageDimensions?.width && imageDimensions?.height ? (
Expand Down
13 changes: 10 additions & 3 deletions apps/computer-vision/app/ocr/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import ImageWithBboxes2 from '../../components/ImageWithOCRBboxes';
import React, { useContext, useEffect, useState } from 'react';
import { GeneratingContext } from '../../context';
import ScreenWrapper from '../../ScreenWrapper';
import ErrorBanner from '../../components/ErrorBanner';

export default function OCRScreen() {
const [imageUri, setImageUri] = useState('');
const [results, setResults] = useState<any[]>([]);
const [error, setError] = useState<string | null>(null);
const [imageDimensions, setImageDimensions] = useState<{
width: number;
height: number;
Expand All @@ -24,6 +26,10 @@ export default function OCRScreen() {
setGlobalGenerating(model.isGenerating);
}, [model.isGenerating, setGlobalGenerating]);

useEffect(() => {
if (model.error) setError(String(model.error));
}, [model.error]);

const handleCameraPress = async (isCamera: boolean) => {
const image = await getImage(isCamera);
const width = image?.width;
Expand All @@ -41,21 +47,22 @@ export default function OCRScreen() {
const output = await model.forward(imageUri);
setResults(output);
} catch (e) {
console.error(e);
setError(e instanceof Error ? e.message : String(e));
}
};

if (!model.isReady) {
if (!model.isReady && !model.error) {
return (
<Spinner
visible={!model.isReady}
visible={true}
textContent={`Loading the model ${(model.downloadProgress * 100).toFixed(0)} %`}
/>
);
}

return (
<ScreenWrapper>
<ErrorBanner message={error} onDismiss={() => setError(null)} />
<View style={styles.container}>
<View style={styles.imageContainer}>
{imageUri && imageDimensions?.width && imageDimensions?.height ? (
Expand Down
26 changes: 19 additions & 7 deletions apps/computer-vision/app/semantic_segmentation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { View, StyleSheet, Image } from 'react-native';
import React, { useContext, useEffect, useState } from 'react';
import { GeneratingContext } from '../../context';
import ScreenWrapper from '../../ScreenWrapper';
import ErrorBanner from '../../components/ErrorBanner';

const numberToColor: number[][] = [
[255, 87, 51], // 0 Red
Expand Down Expand Up @@ -44,19 +45,29 @@ const numberToColor: number[][] = [

export default function SemanticSegmentationScreen() {
const { setGlobalGenerating } = useContext(GeneratingContext);
const { isReady, isGenerating, downloadProgress, forward } =
useSemanticSegmentation({
model: DEEPLAB_V3_MOBILENET_V3_LARGE_QUANTIZED,
});
const {
isReady,
isGenerating,
downloadProgress,
forward,
error: modelError,
} = useSemanticSegmentation({
model: DEEPLAB_V3_MOBILENET_V3_LARGE_QUANTIZED,
});
const [imageUri, setImageUri] = useState('');
const [imageSize, setImageSize] = useState({ width: 0, height: 0 });
const [segImage, setSegImage] = useState<SkImage | null>(null);
const [canvasSize, setCanvasSize] = useState({ width: 0, height: 0 });
const [error, setError] = useState<string | null>(null);

useEffect(() => {
setGlobalGenerating(isGenerating);
}, [isGenerating, setGlobalGenerating]);

useEffect(() => {
if (modelError) setError(String(modelError));
}, [modelError]);

const handleCameraPress = async (isCamera: boolean) => {
const image = await getImage(isCamera);
if (!image?.uri) return;
Expand Down Expand Up @@ -100,21 +111,22 @@ export default function SemanticSegmentationScreen() {
);
setSegImage(img);
} catch (e) {
console.error(e);
setError(e instanceof Error ? e.message : String(e));
}
};

if (!isReady) {
if (!isReady && !modelError) {
return (
<Spinner
visible={!isReady}
visible={true}
textContent={`Loading the model ${(downloadProgress * 100).toFixed(0)} %`}
/>
);
}

return (
<ScreenWrapper>
<ErrorBanner message={error} onDismiss={() => setError(null)} />
<View style={styles.imageCanvasContainer}>
<View style={styles.imageContainer}>
<Image
Expand Down
13 changes: 10 additions & 3 deletions apps/computer-vision/app/style_transfer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { View, StyleSheet, Image } from 'react-native';
import React, { useContext, useEffect, useState } from 'react';
import { GeneratingContext } from '../../context';
import ScreenWrapper from '../../ScreenWrapper';
import ErrorBanner from '../../components/ErrorBanner';

export default function StyleTransferScreen() {
const model = useStyleTransfer({ model: STYLE_TRANSFER_CANDY_QUANTIZED });
Expand All @@ -17,8 +18,13 @@ export default function StyleTransferScreen() {
setGlobalGenerating(model.isGenerating);
}, [model.isGenerating, setGlobalGenerating]);

useEffect(() => {
if (model.error) setError(String(model.error));
}, [model.error]);

const [imageUri, setImageUri] = useState('');
const [styledUri, setStyledUri] = useState('');
const [error, setError] = useState<string | null>(null);

const handleCameraPress = async (isCamera: boolean) => {
const image = await getImage(isCamera);
Expand All @@ -35,22 +41,23 @@ export default function StyleTransferScreen() {
const uri = await model.forward(imageUri, 'url');
setStyledUri(uri);
} catch (e) {
console.error(e);
setError(e instanceof Error ? e.message : String(e));
}
}
};

if (!model.isReady) {
if (!model.isReady && !model.error) {
return (
<Spinner
visible={!model.isReady}
visible={true}
textContent={`Loading the model ${(model.downloadProgress * 100).toFixed(0)} %`}
/>
);
}

return (
<ScreenWrapper>
<ErrorBanner message={error} onDismiss={() => setError(null)} />
<View style={styles.imageContainer}>
<Image
style={styles.image}
Expand Down
13 changes: 10 additions & 3 deletions apps/computer-vision/app/text_to_image/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { GeneratingContext } from '../../context';
import ColorPalette from '../../colors';
import ProgressBar from '../../components/ProgressBar';
import { BottomBarWithTextInput } from '../../components/BottomBarWithTextInput';
import ErrorBanner from '../../components/ErrorBanner';

export default function TextToImageScreen() {
const [inferenceStepIdx, setInferenceStepIdx] = useState<number>(0);
Expand All @@ -21,6 +22,7 @@ export default function TextToImageScreen() {
const [steps, setSteps] = useState<number>(40);
const [showTextInput, setShowTextInput] = useState(false);
const [keyboardVisible, setKeyboardVisible] = useState(false);
const [error, setError] = useState<string | null>(null);

const imageSize = 224;
const model = useTextToImage({
Expand All @@ -34,6 +36,10 @@ export default function TextToImageScreen() {
setGlobalGenerating(model.isGenerating);
}, [model.isGenerating, setGlobalGenerating]);

useEffect(() => {
if (model.error) setError(String(model.error));
}, [model.error]);

useEffect(() => {
const showSub = Keyboard.addListener('keyboardDidShow', () => {
setKeyboardVisible(true);
Expand All @@ -60,18 +66,18 @@ export default function TextToImageScreen() {
}
setImage(output);
} catch (e) {
console.error(e);
setError(e instanceof Error ? e.message : String(e));
setImageTitle(null);
} finally {
setInferenceStepIdx(0);
}
};

if (!model.isReady) {
if (!model.isReady && !model.error) {
// TODO: Update when #614 merged
return (
<Spinner
visible={!model.isReady}
visible={true}
textContent={`Loading the model ${(model.downloadProgress * 100).toFixed(0)} %`}
/>
);
Expand All @@ -87,6 +93,7 @@ export default function TextToImageScreen() {
<View style={styles.container}>
{keyboardVisible && <View style={styles.overlay} />}

<ErrorBanner message={error} onDismiss={() => setError(null)} />
<View style={styles.titleContainer}>
{imageTitle && <Text style={styles.titleText}>{imageTitle}</Text>}
</View>
Expand Down
Loading
Loading