From cd4f323c4be23d08aacc1bb3b7ea5742af5470c8 Mon Sep 17 00:00:00 2001 From: Catie Date: Thu, 10 Sep 2026 11:47:40 +0800 Subject: [PATCH] feat: pull models before channel creation + iOS 26 liquid-glass refresh Model pull in the create form: - New POST /api/channel-model-preview lists upstream models from ad-hoc connection params (provider + baseUrl + key) with no saved channel, so the "new channel" form can pull and multi-select models before creating. - ModelPickerModal refactored to take a loadModels() loader, reused by both the channel editor and the create form. iOS 26 liquid-glass visual refresh (light + dark): - Shared frosted-glass material (gradient + backdrop blur + layered shadow + top highlight) applied to the main card families: panels, metrics, channel cards, model cards, settings groups, account sections, check-in card. - Ambient radial color wash behind cards on the console and account/auth pages so the glass reads with depth. - User center enriched: section eyebrows (API Keys / Models), and an empty state for the available-models grid. Co-Authored-By: Claude Opus 4.8 (1M context) --- cmd/capi/main.go | 33 +++++++++++++ src/App.tsx | 76 ++++++++++++++++++++++------- src/styles.css | 124 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 213 insertions(+), 20 deletions(-) diff --git a/cmd/capi/main.go b/cmd/capi/main.go index a64a825..cf07ecf 100644 --- a/cmd/capi/main.go +++ b/cmd/capi/main.go @@ -928,6 +928,7 @@ func (s *Server) registerRoutes(router *gin.Engine) { admin.DELETE("/api-keys/:id", s.deleteAPIKey) admin.GET("/channels", s.listChannels) admin.POST("/channels", s.createChannel) + admin.POST("/channel-model-preview", s.previewChannelModelsFromConnection) admin.POST("/channels/:id/import-openai-accounts", s.importOpenAIAccounts) admin.POST("/channels/:id/openai-oauth/start", s.startOpenAIOAuth) admin.POST("/channels/:id/openai-oauth/complete", s.completeOpenAIOAuth) @@ -3660,6 +3661,38 @@ func (s *Server) previewUpstreamModels(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"models": modelIDs}) } +// previewChannelModelsFromConnection lists upstream models for connection +// parameters that are not yet saved as a channel, so the create form can pull +// and pick models before the channel exists. +func (s *Server) previewChannelModelsFromConnection(c *gin.Context) { + var body struct { + Provider string `json:"provider"` + BaseURL string `json:"baseUrl"` + UpstreamAPIKey string `json:"upstreamApiKey"` + } + if err := c.ShouldBindJSON(&body); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": gin.H{"message": "Invalid JSON body"}}) + return + } + channel := Channel{ + Provider: strings.TrimSpace(body.Provider), + BaseURL: strings.TrimSpace(body.BaseURL), + } + upstreamKey := "" + for _, line := range strings.Split(body.UpstreamAPIKey, "\n") { + if trimmed := strings.TrimSpace(line); trimmed != "" { + upstreamKey = trimmed + break + } + } + modelIDs, err := s.fetchUpstreamModelIDs(channel, upstreamKey) + if err != nil { + c.JSON(http.StatusBadGateway, gin.H{"error": gin.H{"message": err.Error()}}) + return + } + c.JSON(http.StatusOK, gin.H{"models": modelIDs}) +} + func (s *Server) checkChannel(c *gin.Context) { s.mu.Lock() channel := s.findChannel(c.Param("id")) diff --git a/src/App.tsx b/src/App.tsx index a7f3435..d7aeecc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1673,7 +1673,10 @@ function AccountHome({
-

API 密钥

+
+

API Keys

+

API 密钥

+
{newSecret && {newSecret}} @@ -1694,7 +1697,12 @@ function AccountHome({
-

可用模型

+
+
+

Models

+

可用模型

+
+
{models.map((model) => (
@@ -1704,6 +1712,7 @@ function AccountHome({ {model.id}
))} + {models.length === 0 &&
暂无可用模型,管理员配置渠道后将在此展示
}
@@ -3271,6 +3280,7 @@ function ChannelsView({ const [upstreamApiKey, setUpstreamApiKey] = useState(""); const [message, setMessage] = useState(""); const [busy, setBusy] = useState(false); + const [pickerOpen, setPickerOpen] = useState(false); function applyTemplate(nextProvider: string) { const template = channelTemplateFor(nextProvider); @@ -3358,9 +3368,21 @@ function ChannelsView({
模型 - 来源:模板,可手动修改;保存后可从上游拉取校准 + 可手动填写,或从上游拉取后多选 +
+