-
-
Notifications
You must be signed in to change notification settings - Fork 8
User Interface Updates #741
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,3 +59,6 @@ aef_index.csv | |
| *.log | ||
| deno.json | ||
| edge_function/ | ||
|
|
||
| # clerk configuration (can include secrets) | ||
| /.clerk/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -128,6 +128,9 @@ export const ChatPanel = forwardRef<ChatPanelRef, ChatPanelProps>(({ messages, i | |
| setIsUploading(true) | ||
| try { | ||
| const supabase = getSupabaseBrowserClient() | ||
| if (!supabase) { | ||
| throw new Error('Supabase client is not initialized.') | ||
| } | ||
|
Comment on lines
130
to
+133
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Rollback the optimistic message when upload initialization fails. The optimistic message is added before this guard. When the guard throws, the catch returns while the fake attachment remains in chat history and the selected file remains attached, so retrying can duplicate the message. Either defer 🤖 Prompt for AI Agents |
||
| const fileExt = selectedFile.name.split('.').pop() | ||
| const storagePath = `${crypto.randomUUID()}.${fileExt}` | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,3 @@ | ||||||||||||||||||||||||
| import { Button } from '@/components/ui/button'; | ||||||||||||||||||||||||
| import { Globe, Thermometer, Laptop, HelpCircle } from 'lucide-react'; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| const exampleMessages = [ | ||||||||||||||||||||||||
|
|
@@ -33,23 +32,23 @@ export function EmptyScreen({ | |||||||||||||||||||||||
| }) { | ||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||
| <div className={`mx-auto w-full transition-all overflow-hidden ${className}`}> | ||||||||||||||||||||||||
| <div className="bg-background p-2"> | ||||||||||||||||||||||||
| <div className="mt-4 flex flex-col items-start space-y-2 mb-4"> | ||||||||||||||||||||||||
| <div className="p-2"> | ||||||||||||||||||||||||
| <div className="mt-4 grid grid-cols-1 sm:grid-cols-2 gap-3 mb-4"> | ||||||||||||||||||||||||
| {exampleMessages.map((item) => { | ||||||||||||||||||||||||
| const Icon = item.icon; | ||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||
| <Button | ||||||||||||||||||||||||
| key={item.message} // Use a unique property as the key. | ||||||||||||||||||||||||
| variant="link" | ||||||||||||||||||||||||
| className="h-auto p-0 text-base flex items-center gap-1.5 whitespace-normal text-left break-words max-w-[calc(100vw-2rem)] sm:max-w-full" | ||||||||||||||||||||||||
| name={item.message} | ||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||
| key={item.message} | ||||||||||||||||||||||||
| onClick={async () => { | ||||||||||||||||||||||||
| submitMessage(item.message); | ||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||
|
Comment on lines
+40
to
44
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Add an explicit button type to prevent unintended form submissions. A plain Also, the 💡 Proposed fix- <button
- key={item.message}
- onClick={async () => {
- submitMessage(item.message);
- }}
+ <button
+ key={item.message}
+ type="button"
+ onClick={() => {
+ submitMessage(item.message);
+ }}📝 Committable suggestion
Suggested change
🧰 Tools🪛 React Doctor (0.7.6)[warning] 40-40: Your users can submit the form by accident because a Set an explicit button (button-has-type) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||||||||||||||||
| className="flex items-center gap-3 p-3 text-sm text-left border border-border bg-card hover:bg-accent/50 rounded-xl transition-all w-full text-foreground/80 hover:text-foreground font-medium shadow-sm" | ||||||||||||||||||||||||
| > | ||||||||||||||||||||||||
| <Icon size={16} className="text-muted-foreground flex-shrink-0" /> | ||||||||||||||||||||||||
| {item.heading} | ||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||
| <div className="p-2 bg-secondary rounded-lg text-muted-foreground flex-shrink-0"> | ||||||||||||||||||||||||
| <Icon size={16} /> | ||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||
| <span className="line-clamp-2">{item.heading}</span> | ||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||
| })} | ||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -372,75 +372,86 @@ export const Mapbox: React.FC<{ position?: { latitude: number; longitude: number | |
| // Initialize map (only once) | ||
| useEffect(() => { | ||
| if (mapContainer.current && !map.current) { | ||
| let initialCenter: [number, number] = [position?.longitude ?? 0, position?.latitude ?? 0]; | ||
| let initialZoom = 2; | ||
| let initialPitch = 0; | ||
| let initialBearing = 0; | ||
|
|
||
| if (mapData.cameraState) { | ||
| const { center, range, tilt, heading, zoom, pitch, bearing } = mapData.cameraState; | ||
| initialCenter = [center.lng, center.lat]; | ||
| if (zoom !== undefined) { | ||
| initialZoom = zoom; | ||
| } else if (range !== undefined) { | ||
| initialZoom = Math.log2(40000000 / range); | ||
| try { | ||
| if (!process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN) { | ||
| throw new Error('An API access token is required to use Mapbox GL. Please configure NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN.'); | ||
| } | ||
| initialPitch = pitch ?? tilt ?? 0; | ||
| initialBearing = bearing ?? heading ?? 0; | ||
| } else if (typeof window !== 'undefined' && window.innerWidth < 768) { | ||
| initialZoom = 1.3; | ||
| } | ||
|
|
||
| currentMapCenterRef.current = { center: initialCenter, zoom: initialZoom, pitch: initialPitch }; | ||
|
|
||
| map.current = new mapboxgl.Map({ | ||
| container: mapContainer.current, | ||
| style: 'mapbox://styles/mapbox/satellite-streets-v12', | ||
| center: initialCenter, | ||
| zoom: initialZoom, | ||
| pitch: initialPitch, | ||
| bearing: initialBearing, | ||
| maxZoom: 22, | ||
| attributionControl: true, | ||
| preserveDrawingBuffer: true | ||
| }) | ||
| let initialCenter: [number, number] = [position?.longitude ?? 0, position?.latitude ?? 0]; | ||
| let initialZoom = 2; | ||
| let initialPitch = 0; | ||
| let initialBearing = 0; | ||
|
|
||
| if (mapData.cameraState) { | ||
| const { center, range, tilt, heading, zoom, pitch, bearing } = mapData.cameraState; | ||
| initialCenter = [center.lng, center.lat]; | ||
| if (zoom !== undefined) { | ||
| initialZoom = zoom; | ||
| } else if (range !== undefined) { | ||
| initialZoom = Math.log2(40000000 / range); | ||
| } | ||
| initialPitch = pitch ?? tilt ?? 0; | ||
| initialBearing = bearing ?? heading ?? 0; | ||
| } else if (typeof window !== 'undefined' && window.innerWidth < 768) { | ||
| initialZoom = 1.3; | ||
| } | ||
|
|
||
| // Register event listeners | ||
| map.current.on('moveend', captureMapCenter) | ||
| map.current.on('mousedown', handleUserInteraction) | ||
| map.current.on('touchstart', handleUserInteraction) | ||
| map.current.on('wheel', handleUserInteraction) | ||
| map.current.on('drag', handleUserInteraction) | ||
| map.current.on('zoom', handleUserInteraction) | ||
|
|
||
| map.current.on('load', () => { | ||
| if (!map.current) return | ||
| setMap(map.current) // Set map instance in context | ||
|
|
||
| // Add terrain and sky | ||
| map.current.addSource('mapbox-dem', { | ||
| type: 'raster-dem', | ||
| url: 'mapbox://mapbox.mapbox-terrain-dem-v1', | ||
| tileSize: 512, | ||
| maxzoom: 14, | ||
| currentMapCenterRef.current = { center: initialCenter, zoom: initialZoom, pitch: initialPitch }; | ||
|
|
||
| map.current = new mapboxgl.Map({ | ||
| container: mapContainer.current, | ||
| style: 'mapbox://styles/mapbox/satellite-streets-v12', | ||
| center: initialCenter, | ||
| zoom: initialZoom, | ||
| pitch: initialPitch, | ||
| bearing: initialBearing, | ||
| maxZoom: 22, | ||
| attributionControl: true, | ||
| preserveDrawingBuffer: true | ||
| }) | ||
|
|
||
| map.current.setTerrain({ source: 'mapbox-dem', exaggeration: 1.5 }) | ||
| // Register event listeners | ||
| map.current.on('moveend', captureMapCenter) | ||
| map.current.on('mousedown', handleUserInteraction) | ||
| map.current.on('touchstart', handleUserInteraction) | ||
| map.current.on('wheel', handleUserInteraction) | ||
| map.current.on('drag', handleUserInteraction) | ||
| map.current.on('zoom', handleUserInteraction) | ||
|
|
||
| map.current.on('load', () => { | ||
| if (!map.current) return | ||
| setMap(map.current) // Set map instance in context | ||
|
|
||
| // Add terrain and sky | ||
| map.current.addSource('mapbox-dem', { | ||
| type: 'raster-dem', | ||
| url: 'mapbox://mapbox.mapbox-terrain-dem-v1', | ||
| tileSize: 512, | ||
| maxzoom: 14, | ||
| }) | ||
|
|
||
| map.current.addLayer({ | ||
| id: 'sky', | ||
| type: 'sky', | ||
| paint: { | ||
| 'sky-type': 'atmosphere', | ||
| 'sky-atmosphere-sun': [0.0, 0.0], | ||
| 'sky-atmosphere-sun-intensity': 15, | ||
| }, | ||
| }) | ||
| map.current.setTerrain({ source: 'mapbox-dem', exaggeration: 1.5 }) | ||
|
|
||
| map.current.addLayer({ | ||
| id: 'sky', | ||
| type: 'sky', | ||
| paint: { | ||
| 'sky-type': 'atmosphere', | ||
| 'sky-atmosphere-sun': [0.0, 0.0], | ||
| 'sky-atmosphere-sun-intensity': 15, | ||
| }, | ||
| }) | ||
|
|
||
| initializedRef.current = true | ||
| setIsMapReady(true) | ||
| setIsMapLoaded(true) // Set map loaded state to true | ||
| }) | ||
| } catch (error) { | ||
| console.error('Failed to initialize Mapbox Map:', error) | ||
| initializedRef.current = true | ||
| setIsMapReady(true) | ||
| setIsMapLoaded(true) // Set map loaded state to true | ||
| }) | ||
| setIsMapLoaded(true) // Set loaded/ready so overlay disappears and app remains functional | ||
|
Comment on lines
+445
to
+453
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value Remove the unused The While it might seem tempting to add this ref to the initialization guard ( 🧹 Proposed cleanup- initializedRef.current = true
setIsMapReady(true)
setIsMapLoaded(true) // Set map loaded state to true
})
} catch (error) {
console.error('Failed to initialize Mapbox Map:', error)
- initializedRef.current = true
setIsMapReady(true)
setIsMapLoaded(true) // Set loaded/ready so overlay disappears and app remains functional
}(You can also safely remove its declaration 🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
|
|
||
| return () => { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.