"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; import { Loader2, Package, PlugZap, Settings2, ShieldCheck, UserRound } from "lucide-react"; import { ApiKeysForm } from "@/components/settings/api-keys-form"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { PageHeader } from "@/components/layout/page-header"; import { ThemeToggle } from "@/components/theme-toggle"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { InlineAlert } from "@/components/ui/inline-alert"; import { PROVIDER_OPTIONS } from "@/lib/ai/provider"; import type { ProviderApiKeys, ProviderId } from "@/lib/ai/keys"; import { useActionFeedback } from "@/lib/use-action-feedback"; interface SettingsData { aiProvider: string; aiModel: string; researchAiProvider?: string | null; researchAiModel?: string | null; draftsPerScan: number; matrixRows: number; scanCron: string; apiKeys?: ProviderApiKeys; apiKeysConfigured?: Partial>; } export default function SettingsPage() { const [settings, setSettings] = useState(null); const [apiKeyInputs, setApiKeyInputs] = useState({}); const [saving, setSaving] = useState(false); const [loadError, setLoadError] = useState(null); const { feedback, clearFeedback, showError, showSuccess } = useActionFeedback(); async function load() { try { const res = await fetch("/api/settings"); const data = (await res.json().catch(() => ({}))) as SettingsData; if (!res.ok || !data.aiProvider) { setLoadError("無法載入設定,請重新整理頁面"); return; } setSettings(data); setApiKeyInputs(data.apiKeys ?? {}); } catch { setLoadError("網路連線異常,請重新整理頁面"); } } useEffect(() => { load(); }, []); async function handleSave() { if (!settings) return; clearFeedback(); setSaving(true); try { const res = await fetch("/api/settings", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ aiProvider: settings.aiProvider, aiModel: settings.aiModel, researchAiProvider: settings.researchAiProvider, researchAiModel: settings.researchAiModel, draftsPerScan: settings.draftsPerScan, matrixRows: settings.matrixRows, scanCron: settings.scanCron, apiKeys: apiKeyInputs, }), }); const data = await res.json().catch(() => ({})); if (!res.ok) { showError(data.error ?? "無法儲存設定", "儲存失敗"); return; } setSettings(data); showSuccess("設定已儲存"); } catch { showError("網路連線異常,請稍後再試", "儲存失敗"); } finally { setSaving(false); } } if (loadError) { return (
); } if (!settings) { return (
); } const provider = PROVIDER_OPTIONS.find((p) => p.value === settings.aiProvider); const researchProvider = PROVIDER_OPTIONS.find( (p) => p.value === (settings.researchAiProvider ?? settings.aiProvider) ); const currentProviderConfigured = settings.apiKeysConfigured?.[settings.aiProvider as ProviderId]; return (
{feedback && ( )}
帳號保護模式 瀏覽器海巡固定採用保守限制;無法保證零風險,遇到平台警告會立即停止。
單工執行同時只開一個抓取頁
每次最多 4 任務每個任務最多 12 篇
留言最多 4 × 5只抓高分貼文
每日最多 40 頁限流/驗證即停

連線設定

Chrome 同步、Threads API、搜尋來源

人設設定

控制語氣、定位與禁用詞

品牌與產品

找 TA 的置入話術依據

外觀 深色或淺色,保存在此裝置 AI API Key 所有 Threads 經營帳號共用。目前選用的服務 {currentProviderConfigured ? " 已設定 key。" : " 尚未設定 key。"} setApiKeyInputs((prev) => ({ ...prev, [providerId]: value })) } /> 預設模型 草稿、留言回覆與獲客留言使用的 AI 模型。
產文偏好 影響每次海巡後生成草稿的數量與排程。
setSettings({ ...settings, draftsPerScan: parseInt(event.target.value, 10) || 4, }) } />
setSettings({ ...settings, matrixRows: parseInt(event.target.value, 10) || 7, }) } />
setSettings({ ...settings, scanCron: event.target.value })} placeholder="0 9 * * *" className="font-mono" />
查證搜尋 知識型貼文發布前會自動查證。

海巡搜尋來源可在「連線設定」選擇:混合模式、僅 Threads API、僅 Brave、僅爬蟲,或自訂組合。

.env 設定 BRAVE_SEARCH_API_KEY Brave Search API )。置入海巡預設最多 8 次 Brave 查詢,可用 SCAN_BRAVE_MAX_QUERIES 調整。

); }