-
-
Notifications
You must be signed in to change notification settings - Fork 10.3k
feat(providers): custom icon URL for compatible provider nodes #5815
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
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 | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -56,7 +56,8 @@ export async function PUT(request: Request, { params }: { params: Promise<{ id: | |||||||||||||
| if (isValidationFailure(validation)) { | ||||||||||||||
| return NextResponse.json({ error: validation.error }, { status: 400 }); | ||||||||||||||
| } | ||||||||||||||
| const { name, prefix, apiType, baseUrl, chatPath, modelsPath, customHeaders } = validation.data; | ||||||||||||||
| const { name, prefix, apiType, baseUrl, chatPath, modelsPath, customHeaders, iconUrl } = | ||||||||||||||
| validation.data; | ||||||||||||||
| const node: any = await getProviderNodeById(id); | ||||||||||||||
|
|
||||||||||||||
| if (!node) { | ||||||||||||||
|
|
@@ -93,6 +94,9 @@ export async function PUT(request: Request, { params }: { params: Promise<{ id: | |||||||||||||
| baseUrl: sanitizedBaseUrl, | ||||||||||||||
| chatPath: chatPath || null, | ||||||||||||||
| modelsPath: isClaudeCodeCompatibleProvider(id) ? null : modelsPath || null, | ||||||||||||||
| // #2166: explicit null (not omission) so an empty submission clears a | ||||||||||||||
| // previously stored custom icon. | ||||||||||||||
| iconUrl: iconUrl?.trim() || null, | ||||||||||||||
|
Comment on lines
+97
to
+99
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. In the
Suggested change
|
||||||||||||||
| customHeaders: customHeaders || null, | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| -- Add icon_url column to provider_nodes | ||
| -- Stores an optional operator-supplied remote icon URL for OpenAI-/Anthropic-compatible | ||
| -- provider nodes. NULL = no custom icon (falls back to the built-in @lobehub/static resolution). | ||
| -- Plain TEXT column (no `_json` suffix) — rowToCamel passes it through as a string as-is. | ||
| ALTER TABLE provider_nodes ADD COLUMN icon_url TEXT; |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -64,15 +64,17 @@ export async function createProviderNode(data: JsonRecord) { | |||||||||||||
| baseUrl: data.baseUrl || null, | ||||||||||||||
| chatPath: data.chatPath || null, | ||||||||||||||
| modelsPath: data.modelsPath || null, | ||||||||||||||
| // Optional operator-supplied remote icon URL (#2166) — plain TEXT, no JSON parsing needed. | ||||||||||||||
| iconUrl: data.iconUrl || null, | ||||||||||||||
| customHeadersJson, | ||||||||||||||
| createdAt: now, | ||||||||||||||
| updatedAt: now, | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| db.prepare( | ||||||||||||||
| ` | ||||||||||||||
| INSERT INTO provider_nodes (id, type, name, prefix, api_type, base_url, chat_path, models_path, custom_headers_json, created_at, updated_at) | ||||||||||||||
| VALUES (@id, @type, @name, @prefix, @apiType, @baseUrl, @chatPath, @modelsPath, @customHeadersJson, @createdAt, @updatedAt) | ||||||||||||||
| INSERT INTO provider_nodes (id, type, name, prefix, api_type, base_url, chat_path, models_path, icon_url, custom_headers_json, created_at, updated_at) | ||||||||||||||
| VALUES (@id, @type, @name, @prefix, @apiType, @baseUrl, @chatPath, @modelsPath, @iconUrl, @customHeadersJson, @createdAt, @updatedAt) | ||||||||||||||
| ` | ||||||||||||||
| ).run(node); | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -119,7 +121,8 @@ export async function updateProviderNode(id: string, data: JsonRecord) { | |||||||||||||
| ` | ||||||||||||||
| UPDATE provider_nodes SET type = @type, name = @name, prefix = @prefix, | ||||||||||||||
| api_type = @apiType, base_url = @baseUrl, chat_path = @chatPath, | ||||||||||||||
| models_path = @modelsPath, custom_headers_json = @customHeadersJson, updated_at = @updatedAt | ||||||||||||||
| models_path = @modelsPath, icon_url = @iconUrl, | ||||||||||||||
| custom_headers_json = @customHeadersJson, updated_at = @updatedAt | ||||||||||||||
| WHERE id = @id | ||||||||||||||
| ` | ||||||||||||||
| ).run({ | ||||||||||||||
|
|
@@ -131,6 +134,9 @@ export async function updateProviderNode(id: string, data: JsonRecord) { | |||||||||||||
| baseUrl: merged["baseUrl"] || null, | ||||||||||||||
| chatPath: merged["chatPath"] || null, | ||||||||||||||
| modelsPath: merged["modelsPath"] || null, | ||||||||||||||
| // #2166: iconUrl is nullable — explicit `null` (not omission) clears a previously | ||||||||||||||
| // stored custom icon when the caller submits an empty value. | ||||||||||||||
| iconUrl: merged["iconUrl"] || null, | ||||||||||||||
|
Comment on lines
+137
to
+139
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. In
Suggested change
|
||||||||||||||
| customHeadersJson: merged["customHeadersJson"] || null, | ||||||||||||||
| updatedAt: merged["updatedAt"], | ||||||||||||||
| }); | ||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently,
ProviderIconis only rendered ifproviderNode.iconUrlis set. This creates an inconsistent layout where cards with custom icons are indented, while cards without custom icons are not. RenderingProviderIconunconditionally ensures consistent alignment. By updatingProviderIconto support a text badge fallback when no static icon is found, we can display a clean text badge ("OC", "AC", "CC") as the default fallback for all compatible nodes.