fix: fix gemini models list
This commit is contained in:
@ -74,11 +74,43 @@ func (p *geminiProvider) Summarize(ctx context.Context, prompt string, _ GenOpti
|
|||||||
return result.Candidates[0].Content.Parts[0].Text, nil
|
return result.Candidates[0].Content.Parts[0].Text, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *geminiProvider) ListModels(_ context.Context) ([]string, error) {
|
func (p *geminiProvider) ListModels(ctx context.Context) ([]string, error) {
|
||||||
return []string{
|
url := fmt.Sprintf("https://generativelanguage.googleapis.com/v1beta/models?key=%s", p.apiKey)
|
||||||
"gemini-2.0-flash",
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
"gemini-2.0-flash-lite",
|
if err != nil {
|
||||||
"gemini-1.5-pro",
|
return nil, err
|
||||||
"gemini-1.5-flash",
|
}
|
||||||
}, nil
|
resp, err := p.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
raw, _ := io.ReadAll(resp.Body)
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("gemini list models error %d: %s", resp.StatusCode, raw)
|
||||||
|
}
|
||||||
|
var result struct {
|
||||||
|
Models []struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
SupportedMethods []string `json:"supportedGenerationMethods"`
|
||||||
|
} `json:"models"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(raw, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var names []string
|
||||||
|
for _, m := range result.Models {
|
||||||
|
for _, method := range m.SupportedMethods {
|
||||||
|
if method == "generateContent" {
|
||||||
|
// name is "models/gemini-xxx", strip prefix
|
||||||
|
id := m.Name
|
||||||
|
if len(id) > 7 {
|
||||||
|
id = id[7:] // strip "models/"
|
||||||
|
}
|
||||||
|
names = append(names, id)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ function useTextSelection(containerRef: React.RefObject<HTMLElement>) {
|
|||||||
const [selection, setSelection] = useState<{ text: string; x: number; y: number } | null>(null)
|
const [selection, setSelection] = useState<{ text: string; x: number; y: number } | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function onMouseUp() {
|
function readSelection() {
|
||||||
const sel = window.getSelection()
|
const sel = window.getSelection()
|
||||||
const text = sel?.toString().trim()
|
const text = sel?.toString().trim()
|
||||||
if (!text || !containerRef.current) { setSelection(null); return }
|
if (!text || !containerRef.current) { setSelection(null); return }
|
||||||
@ -28,14 +28,23 @@ function useTextSelection(containerRef: React.RefObject<HTMLElement>) {
|
|||||||
y: rect.top - containerRect.top - 8,
|
y: rect.top - containerRect.top - 8,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function onMouseDown(e: MouseEvent) {
|
function onMouseUp() { readSelection() }
|
||||||
|
// On mobile, selectionchange fires after the user lifts their finger
|
||||||
|
function onSelectionChange() {
|
||||||
|
const sel = window.getSelection()
|
||||||
|
if (!sel?.toString().trim()) setSelection(null)
|
||||||
|
else readSelection()
|
||||||
|
}
|
||||||
|
function onPointerDown(e: PointerEvent) {
|
||||||
if (!(e.target as Element).closest('[data-context-action]')) setSelection(null)
|
if (!(e.target as Element).closest('[data-context-action]')) setSelection(null)
|
||||||
}
|
}
|
||||||
document.addEventListener('mouseup', onMouseUp)
|
document.addEventListener('mouseup', onMouseUp)
|
||||||
document.addEventListener('mousedown', onMouseDown)
|
document.addEventListener('selectionchange', onSelectionChange)
|
||||||
|
document.addEventListener('pointerdown', onPointerDown)
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('mouseup', onMouseUp)
|
document.removeEventListener('mouseup', onMouseUp)
|
||||||
document.removeEventListener('mousedown', onMouseDown)
|
document.removeEventListener('selectionchange', onSelectionChange)
|
||||||
|
document.removeEventListener('pointerdown', onPointerDown)
|
||||||
}
|
}
|
||||||
}, [containerRef])
|
}, [containerRef])
|
||||||
|
|
||||||
@ -278,11 +287,12 @@ export function Dashboard() {
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
<div ref={summaryRef} className="relative">
|
<div ref={summaryRef} className="relative">
|
||||||
<SummaryContent content={current.content} />
|
<SummaryContent content={current.content} />
|
||||||
|
{/* Desktop: floating button above selection */}
|
||||||
{textSel && (
|
{textSel && (
|
||||||
<button
|
<button
|
||||||
data-context-action
|
data-context-action
|
||||||
onClick={addExcerpt}
|
onClick={addExcerpt}
|
||||||
className="absolute z-10 flex items-center gap-1 rounded-full bg-primary text-primary-foreground text-xs px-2 py-1 shadow-lg hover:bg-primary/90 transition-colors"
|
className="absolute z-10 hidden md:flex items-center gap-1 rounded-full bg-primary text-primary-foreground text-xs px-2 py-1 shadow-lg hover:bg-primary/90 transition-colors"
|
||||||
style={{ left: textSel.x, top: textSel.y, transform: 'translate(-50%, -100%)' }}
|
style={{ left: textSel.x, top: textSel.y, transform: 'translate(-50%, -100%)' }}
|
||||||
>
|
>
|
||||||
<Plus className="h-3 w-3" />
|
<Plus className="h-3 w-3" />
|
||||||
@ -290,6 +300,19 @@ export function Dashboard() {
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{/* Mobile: fixed bottom bar — avoids conflict with native selection menu */}
|
||||||
|
{textSel && (
|
||||||
|
<div className="md:hidden fixed bottom-16 left-0 right-0 z-50 flex justify-center px-4 pointer-events-none">
|
||||||
|
<button
|
||||||
|
data-context-action
|
||||||
|
onClick={addExcerpt}
|
||||||
|
className="pointer-events-auto flex items-center gap-2 rounded-full bg-primary text-primary-foreground text-sm px-4 py-2.5 shadow-xl"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
Ajouter au contexte
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<p className="text-xs text-muted-foreground mt-4 italic">
|
<p className="text-xs text-muted-foreground mt-4 italic">
|
||||||
Sélectionnez du texte pour l'ajouter au contexte, puis posez une question dans le panneau qui apparaît.
|
Sélectionnez du texte pour l'ajouter au contexte, puis posez une question dans le panneau qui apparaît.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user