mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-25 01:45:23 +08:00
fix(web): load installed plugin icons through the authenticated route
Multi-Workspace deployments fail the public plugin icon route closed (404), so plain <img src> icons broke everywhere locally and for the runner picker in production. A shared cached hook fetches the authenticated icon for installed plugins and leaves marketplace surfaces on the cloud URL.
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
||||
Puzzle,
|
||||
} from 'lucide-react';
|
||||
import { getCloudServiceClientSync, systemInfo } from '@/app/infra/http';
|
||||
import { useAuthenticatedPluginIcon } from '@/hooks/useAuthenticatedPluginResource';
|
||||
import { useInstalledPluginIcon } from '@/app/infra/hooks/useInstalledPluginIcon';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import {
|
||||
@@ -39,10 +39,9 @@ export default function ExtensionCardComponent({
|
||||
const { t } = useTranslation();
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const [iconFailed, setIconFailed] = useState(false);
|
||||
const authenticatedIcon = useAuthenticatedPluginIcon(
|
||||
cardVO.author,
|
||||
cardVO.name,
|
||||
cardVO.type === 'plugin',
|
||||
const pluginIconURL = useInstalledPluginIcon(
|
||||
cardVO.type === 'plugin' ? cardVO.author : null,
|
||||
cardVO.type === 'plugin' ? cardVO.name : null,
|
||||
);
|
||||
|
||||
const FallbackIcon =
|
||||
@@ -51,9 +50,8 @@ export default function ExtensionCardComponent({
|
||||
: cardVO.type === 'skill'
|
||||
? Sparkles
|
||||
: Puzzle;
|
||||
const iconSrc =
|
||||
cardVO.type === 'plugin' ? authenticatedIcon.url : cardVO.iconURL;
|
||||
const showFallback = iconFailed || authenticatedIcon.error || !iconSrc;
|
||||
const iconSrc = cardVO.type === 'plugin' ? pluginIconURL : cardVO.iconURL;
|
||||
const showFallback = iconFailed || !iconSrc;
|
||||
|
||||
const getTypeLabel = (type: ExtensionType) => {
|
||||
switch (type) {
|
||||
|
||||
@@ -157,7 +157,9 @@ const PluginInstalledComponent = forwardRef<
|
||||
version: meta.version ?? '',
|
||||
enabled: plugin.enabled,
|
||||
type: marketplacePlugin?.type || 'plugin',
|
||||
iconURL: httpClient.getPluginIconURL(author, name),
|
||||
// Icon is resolved by ExtensionCardComponent through the shared
|
||||
// installed-plugin hook (authenticated fetch in multi-Workspace
|
||||
// deployments); the public /icon route 404s there.
|
||||
install_source: plugin.install_source,
|
||||
install_info: plugin.install_info,
|
||||
status: plugin.status,
|
||||
|
||||
+21
-7
@@ -2,9 +2,16 @@ import { PluginCardVO } from '@/app/home/plugins/components/plugin-installed/Plu
|
||||
import { useState } from 'react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { BugIcon, ExternalLink, Ellipsis, Trash, ArrowUp } from 'lucide-react';
|
||||
import {
|
||||
BugIcon,
|
||||
ExternalLink,
|
||||
Ellipsis,
|
||||
Trash,
|
||||
ArrowUp,
|
||||
Puzzle,
|
||||
} from 'lucide-react';
|
||||
import { getCloudServiceClientSync, systemInfo } from '@/app/infra/http';
|
||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||
import { useInstalledPluginIcon } from '@/app/infra/hooks/useInstalledPluginIcon';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -27,6 +34,7 @@ export default function PluginCardComponent({
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const iconURL = useInstalledPluginIcon(cardVO.author, cardVO.name);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -36,11 +44,17 @@ export default function PluginCardComponent({
|
||||
>
|
||||
<div className="w-full h-full flex flex-row items-start justify-start gap-[1.2rem]">
|
||||
{/* Icon - fixed width */}
|
||||
<img
|
||||
src={httpClient.getPluginIconURL(cardVO.author, cardVO.name)}
|
||||
alt="plugin icon"
|
||||
className="w-16 h-16 rounded-[8%] flex-shrink-0"
|
||||
/>
|
||||
{iconURL ? (
|
||||
<img
|
||||
src={iconURL}
|
||||
alt="plugin icon"
|
||||
className="w-16 h-16 rounded-[8%] flex-shrink-0"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-16 h-16 flex-shrink-0 flex items-center justify-center">
|
||||
<Puzzle className="w-12 h-12 text-blue-500" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Content area - flexible width with min-width to prevent overflow */}
|
||||
<div className="flex-1 min-w-0 h-full flex flex-col items-start justify-between gap-[0.6rem]">
|
||||
|
||||
Reference in New Issue
Block a user