This commit sets up the foundational project structure for PhotumManager. It includes: - Initializing a new React project with Vite. - Configuring essential dependencies such as React, Lucide React, and the Google Generative AI SDK. - Setting up TypeScript and Vite configurations for optimal development. - Defining core application metadata and initial type definitions for users and events. - Establishing basic styling and font configurations in `index.html` with Tailwind CSS. - Adding a `.gitignore` file to manage project dependencies and build artifacts. - Updating the README with instructions for local development.
99 lines
3.1 KiB
TypeScript
99 lines
3.1 KiB
TypeScript
import { GoogleGenAI } from "@google/genai";
|
|
|
|
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
|
|
|
|
export interface GeoResult {
|
|
street: string;
|
|
number: string;
|
|
city: string;
|
|
state: string;
|
|
zip: string;
|
|
description: string;
|
|
mapLink?: string;
|
|
}
|
|
|
|
export const searchLocationWithGemini = async (query: string): Promise<GeoResult[]> => {
|
|
if (!query || query.length < 3) return [];
|
|
|
|
try {
|
|
// Attempt to get user location for better context
|
|
let userLocation: { latitude: number; longitude: number } | undefined;
|
|
try {
|
|
const position = await new Promise<GeolocationPosition>((resolve, reject) => {
|
|
navigator.geolocation.getCurrentPosition(resolve, reject, { timeout: 2000 });
|
|
});
|
|
userLocation = {
|
|
latitude: position.coords.latitude,
|
|
longitude: position.coords.longitude
|
|
};
|
|
} catch (e) {
|
|
// Ignore geolocation errors, proceed without it
|
|
}
|
|
|
|
const response = await ai.models.generateContent({
|
|
model: 'gemini-2.5-flash',
|
|
contents: `Find the specific address details for the location query: "${query}".
|
|
|
|
You MUST return the address components in the following format (one per line):
|
|
Description: [A concise formatted address]
|
|
Street: [Street Name]
|
|
Number: [Street Number, if available]
|
|
City: [City Name]
|
|
State: [State Code, e.g., SP, RJ]
|
|
Zip: [Postal Code]
|
|
|
|
If a specific component is not found, leave it empty after the colon.`,
|
|
config: {
|
|
tools: [{ googleMaps: {} }],
|
|
toolConfig: userLocation ? {
|
|
retrievalConfig: {
|
|
latLng: {
|
|
latitude: userLocation.latitude,
|
|
longitude: userLocation.longitude
|
|
}
|
|
}
|
|
} : undefined
|
|
}
|
|
});
|
|
|
|
const text = response.text || "";
|
|
|
|
// Helper parser
|
|
const parseField = (key: string) => {
|
|
const regex = new RegExp(`${key}:\\s*(.*)`, 'i');
|
|
const match = text.match(regex);
|
|
return match ? match[1].trim() : '';
|
|
};
|
|
|
|
// Extract Google Maps URI from grounding metadata
|
|
let mapUri = '';
|
|
const chunks = response.candidates?.[0]?.groundingMetadata?.groundingChunks || [];
|
|
|
|
// Look for the Web URI in the chunks (Google Maps grounding often returns this)
|
|
for (const chunk of chunks) {
|
|
if (chunk.web?.uri && (chunk.web.uri.includes('google.com/maps') || chunk.web.uri.includes('maps.google.com'))) {
|
|
mapUri = chunk.web.uri;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const result: GeoResult = {
|
|
street: parseField('Street'),
|
|
number: parseField('Number'),
|
|
city: parseField('City'),
|
|
state: parseField('State'),
|
|
zip: parseField('Zip'),
|
|
description: parseField('Description') || text.split('\n')[0], // Fallback to first line
|
|
mapLink: mapUri
|
|
};
|
|
|
|
// Filter out bad results (empty city/state usually means it failed to identify a place)
|
|
if (!result.city && !result.street) return [];
|
|
|
|
return [result];
|
|
|
|
} catch (error) {
|
|
console.error("Gemini Search Error:", error);
|
|
return [];
|
|
}
|
|
};
|