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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@

This repository contains a sample application demonstrating the use of [**Grounding Lite**](https://developers.google.com/maps/ai/grounding-lite) with the [Gemini API](https://ai.google.dev/gemini-api/docs) to provide spatially grounded responses using Google Maps Platform data. The application uses the agent to converse and a companion 3D Google Maps.

### Real estate focus (this project)

The assistant is aimed at **local real estate exploration** in India with two capabilities:

1. **Neighborhood amenities** — Discover shops, malls, theatres, stores, hospitals, restaurants, meat shops, play areas, gyms, and similar around a locality using Google Maps (`search_places`).
2. **Homes for sale** — Curated **apartments and houses** from Supabase, keyed by locality. The backend connects to Supabase via MCP (read-only) when `SUPABASE_ACCESS_TOKEN` is set.

**Database setup:** Run `scripts/supabase-real-estate-schema.sql` in the [Supabase SQL Editor](https://supabase.com/dashboard) for your linked project. That script drops legacy `dream_stays` / `cities` tables and creates `localities`, `property_listings`, a `listings_by_locality` view, and seed data for Bangalore neighborhoods.

Please refer to the official documentation for more details: [Grounding Lite Documentation](https://developers.google.com/maps/ai/grounding-lite).

## Architecture
Expand Down Expand Up @@ -105,6 +114,13 @@ For the server-side (Maps Platform MCP calls and Gemini API calls), use:
SERVER_API_KEY="YOUR_SERVER_API_KEY_HERE"
```

Optional — Supabase MCP for property listings (same project as in `conversationalAIService.ts` or your own; update the MCP URL if you use a different project):
```
SUPABASE_ACCESS_TOKEN="YOUR_SUPABASE_PERSONAL_ACCESS_TOKEN"
```

Create a personal access token under [Supabase Account → Access Tokens](https://supabase.com/dashboard/account/tokens). Apply `scripts/supabase-real-estate-schema.sql` before relying on listing queries.

## Architecture


Expand Down
934 changes: 851 additions & 83 deletions chat-app.ts

Large diffs are not rendered by default.

111 changes: 93 additions & 18 deletions index.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,96 @@

@import "tailwindcss";

/* LobbyNext theme design tokens */
:root {
--lobbynext-bg: #050816; /* main app background */
--lobbynext-surface: #0f172a; /* cards / panels */
--lobbynext-surface-soft: #111827; /* softer surface */
--lobbynext-accent: #38bdf8; /* primary accent (sky blue) */
--lobbynext-accent-soft: #0ea5e9; /* lighter accent for hovers */
--lobbynext-accent-muted: #1d4ed8; /* secondary accent */
--lobbynext-text: #e5e7eb; /* main text */
--lobbynext-text-soft: #9ca3af; /* secondary text */
--lobbynext-danger: #f97373;
}

/* Ensure chat-app custom element takes full viewport space */
chat-app {
display: block;
height: 100%;
width: 100%;
}

/* Map focus vignette: MUST be a sibling of gmp-map-3d (not inside it) — in-map divs sit under the WebGL canvas. */
#map-focus-vignette-layer {
position: absolute;
inset: 0;
z-index: 5;
pointer-events: none;
overflow: hidden;
isolation: isolate;
/* --focus-hole-r set inline from camera range (chat-app) */
--focus-hole-x: 50%;
--focus-hole-y: 50%;
}

@media (min-width: 640px) {
#map-focus-vignette-layer {
--focus-hole-x: 44%;
}
}

#map-focus-vignette-layer .map-focus-vignette {
position: absolute;
inset: 0;
pointer-events: none;
/* Tint stays subtle; blur strength is moderate — mask ramp does the “gradient blur” feel */
background: rgba(15, 23, 42, 0.05);
/* Wide transition band (px + % of hole) so the frosted layer fades in smoothly, not in one step */
--v-falloff: min(220px, max(100px, calc(var(--focus-hole-r) * 0.62)));
--v-blur: clamp(10px, calc(8px + var(--focus-hole-r) * 0.035), 18px);
-webkit-mask-image: radial-gradient(
circle at var(--focus-hole-x) var(--focus-hole-y),
transparent 0,
transparent max(0px, calc(var(--focus-hole-r) - min(20px, calc(var(--focus-hole-r) * 0.06)))),
rgba(0, 0, 0, 0.06) calc(var(--focus-hole-r) + calc(var(--v-falloff) * 0.08)),
rgba(0, 0, 0, 0.14) calc(var(--focus-hole-r) + calc(var(--v-falloff) * 0.2)),
rgba(0, 0, 0, 0.28) calc(var(--focus-hole-r) + calc(var(--v-falloff) * 0.36)),
rgba(0, 0, 0, 0.46) calc(var(--focus-hole-r) + calc(var(--v-falloff) * 0.54)),
rgba(0, 0, 0, 0.64) calc(var(--focus-hole-r) + calc(var(--v-falloff) * 0.72)),
rgba(0, 0, 0, 0.82) calc(var(--focus-hole-r) + calc(var(--v-falloff) * 0.88)),
#000 calc(var(--focus-hole-r) + var(--v-falloff))
);
mask-image: radial-gradient(
circle at var(--focus-hole-x) var(--focus-hole-y),
transparent 0,
transparent max(0px, calc(var(--focus-hole-r) - min(20px, calc(var(--focus-hole-r) * 0.06)))),
rgba(0, 0, 0, 0.06) calc(var(--focus-hole-r) + calc(var(--v-falloff) * 0.08)),
rgba(0, 0, 0, 0.14) calc(var(--focus-hole-r) + calc(var(--v-falloff) * 0.2)),
rgba(0, 0, 0, 0.28) calc(var(--focus-hole-r) + calc(var(--v-falloff) * 0.36)),
rgba(0, 0, 0, 0.46) calc(var(--focus-hole-r) + calc(var(--v-falloff) * 0.54)),
rgba(0, 0, 0, 0.64) calc(var(--focus-hole-r) + calc(var(--v-falloff) * 0.72)),
rgba(0, 0, 0, 0.82) calc(var(--focus-hole-r) + calc(var(--v-falloff) * 0.88)),
#000 calc(var(--focus-hole-r) + var(--v-falloff))
);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-size: 100% 100%;
mask-size: 100% 100%;
-webkit-mask-mode: alpha;
mask-mode: alpha;
-webkit-backdrop-filter: blur(var(--v-blur)) saturate(1.02);
backdrop-filter: blur(var(--v-blur)) saturate(1.02);
}
/* Material Design 3 Light Theme Scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #F0F2F5; /* Light grey, similar to Surface Container */
background: #020617;
}
::-webkit-scrollbar-thumb {
background: #B8BEC4; /* Medium grey, like Outline */
background: #1f2937;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
Expand All @@ -41,7 +116,7 @@ chat-app {
.place-index-link {
background-color: transparent;
border: none;
color: #006780; /* Primary color */
color: var(--lobbynext-accent);
font-weight: 600; /* semibold */
padding: 0 1px;
margin: 0;
Expand Down Expand Up @@ -78,17 +153,17 @@ chat-app {
margin-bottom: 0.25rem; /* mb-1 */
}
.chat-message-content a {
color: #006780; /* Primary color */
color: var(--lobbynext-accent);
text-decoration: none;
}
.chat-message-content a:hover {
color: #004F63; /* Darker Primary */
color: var(--lobbynext-accent-soft);
text-decoration: underline;
}
.chat-message-content pre {
background-color: #F0F2F5; /* Surface Container Low/Medium */
color: #1A1C1E; /* On Surface */
border: 1px solid #DFE3E8; /* Surface Variant */
background-color: #020617;
color: var(--lobbynext-text);
border: 1px solid #1f2937;
padding: 0.75rem; /* p-3 */
border-radius: 0.375rem; /* rounded-md */
overflow-x: auto;
Expand All @@ -98,23 +173,23 @@ chat-app {
font-size: 0.875rem; /* text-sm */
}
.chat-message-content code:not(pre > code) {
background-color: #E0E2E6; /* Surface Container */
color: #1A1C1E; /* On Surface */
background-color: #020617;
color: var(--lobbynext-accent-soft);
padding: 0.125rem 0.25rem; /* px-1 py-0.5 */
border-radius: 0.25rem; /* rounded-sm */
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 0.875em; /* slightly smaller than surrounding text */
}
.chat-message-content blockquote {
border-left: 4px solid #B8BEC4; /* Outline */
border-left: 4px solid #1f2937;
padding-left: 1rem; /* pl-4 */
margin-left: 0;
margin-right: 0;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
font-style: italic;
color: #42474E; /* On Surface Variant */
background-color: #F0F2F5; /* Surface Container Low */
color: var(--lobbynext-text-soft);
background-color: #020617;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
padding-right: 1rem;
Expand All @@ -124,19 +199,19 @@ chat-app {
border-collapse: collapse;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
border: 1px solid #CFD2D6; /* Light border for table */
border: 1px solid #1f2937;
}
.chat-message-content th, .chat-message-content td {
border: 1px solid #CFD2D6; /* Lighter border */
border: 1px solid #1f2937;
padding: 0.5rem; /* p-2 */
text-align: left;
color: #1A1C1E; /* On Surface */
color: var(--lobbynext-text);
}
.chat-message-content th {
background-color: #E0E2E6; /* Surface Container */
background-color: #020617;
}
.chat-message-content hr {
border-top: 1px solid #CFD2D6; /* Lighter border */
border-top: 1px solid #1f2937;
margin-top: 1rem;
margin-bottom: 1rem;
}
Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Grounding Lite - Sample App</title>
<title>LobbyNext</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
This page provides attribution requirements and guidelines for displaying
Grounding Lite content in your applications.

## Display Google Maps attribution

You must follow Google Maps attribution requirements when displaying Content
from Grounding Lite in your app or website. You don't need to add extra
attribution if the Content is shown on a Google Map where the attribution is
already visible.

## Google Maps logo and text attribution

Attribution should take the form of the Google Maps logo whenever possible. In
cases where space is limited, the text **Google Maps** is acceptable. It must
always be clear to end users which content is provided by Google Maps.
![Left: Google Maps logo attribution, Right: Google Maps text attribution](https://developers.google.com/static/maps/images/01_GMP_Logo_Text.jpg) Left: Google Maps logo attribution, Right: Google Maps text attribution

## Logo attribution

Follow these requirements for using the Google Maps logo in your app or website.
![Acceptable variations for Google Maps logo attribution](https://developers.google.com/static/maps/images/02_GMP_Logo_Alternates.jpg) Acceptable variations for Google Maps logo attribution

### Download Google Maps logos

Use the official Google Maps logo files. Download the logos below, and follow
the guidelines in this section.

[Download the Google Maps attribution assets](https://developers.google.com/static/maps/documentation/images/Google_Maps_Attribution_Assets.zip)

When using the Google Maps logo, follow these guidelines.

- Don't modify the logo in any way.
- Maintain the aspect ratio of the logo to prevent distortion.
- Use the outlined logo on a busy background, like a map or image.
- Use the non-outlined logo on a plain background, like a solid color or subtle gradient.

### Logo size specification

Follow these size specifications for the Google Maps logo:

- **Minimum logo height:** 16dp
- **Maximum logo height:** 19dp
- **Minimum logo clear space:** 10dp on left, right and top, 5dp on the bottom

To learn about dp, see [Pixel
density](https://m2.material.io/design/layout/pixel-density.html#pixel-density)
on the Material Design website.
![Google Maps logo showing minimum clear space and acceptable size range](https://developers.google.com/static/maps/images/03_GMP_Logo_Size_Specs.jpg) Google Maps logo showing minimum clear space and acceptable size range

### Logo accessibility

Follow these accessibility requirements for the Google Maps logo:

- Maintain an [accessible
contrast](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html) between the logo and the background.
- Include an accessibility label with the text **Google Maps**.

![Unacceptable variations and accessibility issues for Google Maps logo attribution](https://developers.google.com/static/maps/images/04_GMP_Accessibility.png) Unacceptable variations and accessibility issues for Google Maps logo attribution

## Text attribution

If the size of your interface does not support using the Google Maps logo, you
can spell out **Google Maps** in text. Follow these guidelines:
![Acceptable variations of the Google Maps text attribution](https://developers.google.com/static/maps/images/05_GMP_Text_Attribution.jpg) Acceptable variations of the Google Maps text attribution

- Don't modify the text **Google Maps** in any way:
- Don't change the capitalization of **Google Maps**
- Don't wrap **Google Maps** onto multiple lines
- Don't localize **Google Maps** into another language.
- Prevent browsers from translating **Google Maps** by using the HTML attribute `translate="no"`.

![Unacceptable variations of the Google Maps text attribution](https://developers.google.com/static/maps/images/06_GMP_Text_Donts.jpg) Unacceptable variations of the Google Maps text attribution

- Style Google Maps text as described in the following table:

| Google Maps text-styling requirements ||
|---|---|
| **Property** | **Style** |
| Font family | [Roboto](https://fonts.google.com/specimen/Roboto?preview.text_type=custom). Loading the font is optional. |
| Fallback font family | Any sans serif body font already used in your product or "Sans-Serif" to invoke the default system font |
| Font style | Normal |
| Font weight | 400 |
| Font color | White, black (#1F1F1F), or gray (#5E5E5E). Maintain accessible [(4.5:1)](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html) contrast against the background. |
| Font size | Minimum font size: 12sp Maximum font size: 16sp To learn about sp, see [Font size units](https://m3.material.io/styles/typography/type-scale-tokens#3f4488e7-3b74-45b0-a143-9d6afa4d62dc) on the Material Design website. |
| Letter spacing | Normal |

### Example CSS

The following CSS renders Google Maps with the appropriate typographic style and
color on a white or light background.

```
@import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');

.GMP-attribution {
font-family: Roboto, Sans-Serif;
font-style: normal;
font-weight: 400;
font-size: 1rem;
letter-spacing: normal;
white-space: nowrap;
color: #5e5e5e;
}
```
Loading