@plannotator/ui 0.53.1 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HANDOFF.md +217 -11
- package/README.md +65 -10
- package/components/AgentToolAnnouncementDialog.tsx +1 -1
- package/components/AgentsTab.tsx +1 -1
- package/components/AnnotationPanel.tsx +142 -24
- package/components/AnnotationSidebar.tsx +1 -1
- package/components/AnnotationToolbar.tsx +64 -9
- package/components/ApproveDropdown.tsx +7 -9
- package/components/AskSessionAnnouncementDialog.tsx +1 -1
- package/components/AttachmentsButton.tsx +1 -1
- package/components/BlockRenderer.tsx +19 -5
- package/components/CodeFilePopout.tsx +139 -4
- package/components/CommentPopover.tsx +44 -8
- package/components/DecisionControl.tsx +31 -5
- package/components/DiagramBlock.tsx +17 -12
- package/components/EditorAnnotationCard.tsx +1 -1
- package/components/ExportModal.tsx +77 -7
- package/components/HostTitle.tsx +0 -4
- package/components/ImageAnnotator/Toolbar.tsx +25 -1
- package/components/ImageAnnotator/index.tsx +24 -4
- package/components/ImageLightbox.tsx +658 -120
- package/components/ImagePeek.tsx +38 -4
- package/components/ImageRegions.tsx +369 -0
- package/components/ImageThumbnail.tsx +1 -1
- package/components/ImageZoomStage.tsx +796 -0
- package/components/InboxAnnouncementDialog.tsx +202 -0
- package/components/InlineMarkdown.tsx +152 -24
- package/components/KeyboardShortcuts.tsx +23 -0
- package/components/LazyBlockBoundary.tsx +100 -0
- package/components/ListMarker.tsx +3 -3
- package/components/LookAndFeelAnnouncementDialog.tsx +12 -2
- package/components/MenuVersionSection.tsx +31 -9
- package/components/PlanDecisionMenuExtras.tsx +129 -0
- package/components/PlanHeaderMenu.tsx +15 -1
- package/components/ProviderIcons.tsx +8 -0
- package/components/RefHoverCard.tsx +913 -0
- package/components/SendFailureToast.tsx +126 -0
- package/components/Settings.tsx +617 -126
- package/components/TerminalToolsAnnouncementDialog.tsx +1 -1
- package/components/ThemeProvider.tsx +14 -1
- package/components/ThemeTab.tsx +16 -24
- package/components/Tooltip.tsx +49 -18
- package/components/Viewer.tsx +280 -41
- package/components/ai/DocumentAIChatPanel.tsx +29 -3
- package/components/ai/NewChatButton.tsx +74 -0
- package/components/blocks/CodeBlock.tsx +1 -1
- package/components/blocks/HtmlBlock.tsx +7 -1
- package/components/blocks/QuestionBlock.tsx +522 -62
- package/components/diagram/DiagramComposer.tsx +5 -1
- package/components/diagram/DiagramPending.tsx +12 -2
- package/components/diagram/DiagramViewer.tsx +19 -3
- package/components/html-embed/HtmlEmbedBlock.tsx +374 -0
- package/components/html-embed/SvgFenceBlock.tsx +46 -0
- package/components/html-embed/index.ts +3 -0
- package/components/html-embed/languages.ts +15 -0
- package/components/html-embed/loadGate.ts +107 -0
- package/components/html-viewer/HtmlViewer.tsx +47 -6
- package/components/html-viewer/bridge-script.asset.js +173 -1
- package/components/html-viewer/bridge-script.ts +173 -1
- package/components/html-viewer/useHtmlAnnotation.ts +125 -0
- package/components/imageZoom.ts +265 -0
- package/components/plan-diff/PlanCleanDiffView.tsx +1 -1
- package/components/plan-diff/PlanDiffViewer.tsx +13 -2
- package/components/plan-diff/PlanRawDiffView.tsx +1 -1
- package/components/sidebar/FileBrowser.tsx +16 -2
- package/components/sidebar/SidebarTabs.tsx +3 -0
- package/config/index.ts +3 -0
- package/config/reviewView.ts +41 -1
- package/config/settings.ts +155 -4
- package/configure.ts +28 -0
- package/hooks/pfm/useCodeFilePopout.ts +7 -1
- package/hooks/useAIChat.ts +72 -25
- package/hooks/useAnnotationDraft.ts +71 -6
- package/hooks/useAnnotationHighlighter.ts +57 -3
- package/hooks/useCodeAnnotationDraft.ts +113 -17
- package/hooks/useCookieHostSetting.ts +96 -0
- package/hooks/useDocumentFont.ts +17 -0
- package/hooks/useDocumentTextSize.ts +20 -0
- package/hooks/useFileBrowser.ts +10 -1
- package/hooks/useLinkedDoc.ts +226 -13
- package/hooks/usePackageChangesSetting.ts +90 -0
- package/hooks/usePlanLookDialog.ts +67 -0
- package/hooks/useSharing.ts +11 -7
- package/hooks/useThemePaletteHalves.ts +37 -0
- package/hooks/useUpdateCheck.ts +22 -4
- package/hooks/useUpdateToast.ts +1 -1
- package/package.json +23 -22
- package/shortcuts/code-review/annotationToolbar.shortcuts.ts +1 -0
- package/shortcuts/code-review/explorer.shortcuts.ts +82 -0
- package/shortcuts/core.ts +63 -4
- package/shortcuts/index.ts +1 -0
- package/shortcuts/plan-review/annotationPanel.shortcuts.ts +1 -1
- package/shortcuts/plan-review/commentPopover.shortcuts.ts +1 -0
- package/shortcuts/runtime.ts +4 -3
- package/styles.css +1 -1
- package/theme.css +179 -1
- package/themes/plannotator-graphite.css +66 -0
- package/types.ts +26 -9
- package/utils/agentSwitch.ts +41 -20
- package/utils/agentToolSetting.ts +1 -1
- package/utils/aiChatFormat.ts +23 -7
- package/utils/annotationOwnsHighlight.ts +4 -2
- package/utils/asciidocParser.ts +570 -0
- package/utils/autoUpdateNotice.ts +24 -0
- package/utils/belowTitleSlot.ts +174 -0
- package/utils/codeLineScroll.ts +100 -0
- package/utils/commentSubmitKey.ts +48 -0
- package/utils/cssFontFamily.ts +65 -0
- package/utils/decisionSpec.ts +190 -2
- package/utils/defaultNotesApp.ts +1 -1
- package/utils/diffFonts.ts +4 -1
- package/utils/documentFont.ts +68 -0
- package/utils/documentTextSize.ts +54 -0
- package/utils/fontDetect.ts +63 -0
- package/utils/inboxAnnouncement.ts +78 -0
- package/utils/lookAndFeelAnnouncement.ts +82 -5
- package/utils/markdownExtensions.ts +2 -2
- package/utils/mermaid.ts +20 -3
- package/utils/mermaidTheme.ts +3 -51
- package/utils/notion.ts +58 -0
- package/utils/parser.ts +28 -1
- package/utils/platform.ts +46 -0
- package/utils/refCard.ts +82 -0
- package/utils/richClipboard.ts +380 -0
- package/utils/richClipboardLists.ts +57 -0
- package/utils/richCopyPolicy.ts +22 -0
- package/utils/scrollAnchor.ts +82 -0
- package/utils/sectionRefs.ts +123 -0
- package/utils/selectionBoundary.ts +40 -0
- package/utils/sendFailure.ts +85 -0
- package/utils/sharing.ts +5 -2
- package/utils/themeRegistry.ts +10 -0
- package/utils/themeTokens.ts +74 -0
- package/utils/updateNotice.ts +30 -8
- package/utils/vscodePanel.ts +9 -0
|
@@ -13,6 +13,14 @@ function fallbackCommand(origin?: Origin | null, isWSL = false): string {
|
|
|
13
13
|
: 'curl -fsSL https://plannotator.ai/install.sh | bash';
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/** "Oct 26, 9:14 AM", from the server's ISO time. */
|
|
17
|
+
function failingSince(iso: string): string {
|
|
18
|
+
const date = new Date(iso);
|
|
19
|
+
return Number.isNaN(date.getTime())
|
|
20
|
+
? iso
|
|
21
|
+
: date.toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
|
|
22
|
+
}
|
|
23
|
+
|
|
16
24
|
interface MenuVersionSectionProps {
|
|
17
25
|
appVersion: string;
|
|
18
26
|
updateInfo?: UpdateInfo | null;
|
|
@@ -37,6 +45,8 @@ export const MenuVersionSection: React.FC<MenuVersionSectionProps> = ({
|
|
|
37
45
|
const [copied, setCopied] = useState(false);
|
|
38
46
|
const [updating, setUpdating] = useState<'idle' | 'starting' | 'started' | string>('idle');
|
|
39
47
|
const hasUpdate = !!updateInfo?.updateAvailable;
|
|
48
|
+
// Checking for updates has failed for hours: a quiet line and the command (no dot, no toast).
|
|
49
|
+
const checkFailing = updateInfo?.checkFailing ?? null;
|
|
40
50
|
const command = updateInfo?.command ?? fallbackCommand(origin, isWSL);
|
|
41
51
|
|
|
42
52
|
const handleCopy = async () => {
|
|
@@ -86,17 +96,29 @@ export const MenuVersionSection: React.FC<MenuVersionSectionProps> = ({
|
|
|
86
96
|
{hasUpdate ? "What's new" : 'Release notes'}
|
|
87
97
|
</a>
|
|
88
98
|
</span>
|
|
99
|
+
{!hasUpdate && checkFailing && (
|
|
100
|
+
<span className="text-muted-foreground" data-menu-update-check-failing="">
|
|
101
|
+
Can't check for updates since {failingSince(checkFailing.since)} ({checkFailing.error}). To update now:
|
|
102
|
+
</span>
|
|
103
|
+
)}
|
|
104
|
+
{(hasUpdate || checkFailing) && (
|
|
105
|
+
<div className="w-full mt-1 flex items-center gap-1.5 rounded-md border border-border bg-background px-2 py-1" data-menu-update-command="">
|
|
106
|
+
{/* Shown whole when checking fails (it is the only way forward there); one scrolling line beside an update, as before. */}
|
|
107
|
+
<code
|
|
108
|
+
className={`flex-1 min-w-0 font-mono text-[10px] text-foreground ${hasUpdate ? 'overflow-x-auto whitespace-nowrap' : 'whitespace-pre-wrap break-all'}`}
|
|
109
|
+
>
|
|
110
|
+
{command}
|
|
111
|
+
</code>
|
|
112
|
+
<button
|
|
113
|
+
onClick={handleCopy}
|
|
114
|
+
className="shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium border border-border hover:bg-muted transition-colors"
|
|
115
|
+
>
|
|
116
|
+
{copied ? 'Copied' : 'Copy'}
|
|
117
|
+
</button>
|
|
118
|
+
</div>
|
|
119
|
+
)}
|
|
89
120
|
{hasUpdate && (
|
|
90
121
|
<>
|
|
91
|
-
<div className="w-full mt-1 flex items-center gap-1.5 rounded-md border border-border bg-background px-2 py-1">
|
|
92
|
-
<code className="flex-1 min-w-0 overflow-x-auto whitespace-nowrap font-mono text-[10px] text-foreground">{command}</code>
|
|
93
|
-
<button
|
|
94
|
-
onClick={handleCopy}
|
|
95
|
-
className="shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium border border-border hover:bg-muted transition-colors"
|
|
96
|
-
>
|
|
97
|
-
{copied ? 'Copied' : 'Copy'}
|
|
98
|
-
</button>
|
|
99
|
-
</div>
|
|
100
122
|
{updateInfo!.action === 'script' && (
|
|
101
123
|
<button
|
|
102
124
|
onClick={handleUpdate}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Check } from 'lucide-react';
|
|
3
|
+
import type { Agent } from '../hooks/useAgents';
|
|
4
|
+
import { useConfigValue } from '../config/useConfig';
|
|
5
|
+
import {
|
|
6
|
+
getAgentSwitchDefaults,
|
|
7
|
+
saveAgentSwitchSettings,
|
|
8
|
+
type AgentSwitchSettings,
|
|
9
|
+
} from '../utils/agentSwitch';
|
|
10
|
+
import { ActionMenuDivider } from './ActionMenu';
|
|
11
|
+
import { cn } from '../lib/utils';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Rows plan review puts under the decisions in DecisionControl's menu
|
|
15
|
+
* (`menuFooter`, #1057). Neither is a decision: picking an agent keeps the
|
|
16
|
+
* menu open, and the permission line only reports what Approve already sends.
|
|
17
|
+
*
|
|
18
|
+
* NOT host-supported surface (like DecisionControl): Plannotator's own plan
|
|
19
|
+
* review chrome.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
function isSelected(agentId: string, setting: AgentSwitchSettings): boolean {
|
|
23
|
+
if (setting.switchTo === 'custom' || setting.switchTo === 'disabled') return false;
|
|
24
|
+
return agentId.toLowerCase() === setting.switchTo.toLowerCase();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const RadioRow: React.FC<{
|
|
28
|
+
checked: boolean;
|
|
29
|
+
onSelect: () => void;
|
|
30
|
+
children: React.ReactNode;
|
|
31
|
+
note?: string;
|
|
32
|
+
}> = ({ checked, onSelect, children, note }) => (
|
|
33
|
+
<button
|
|
34
|
+
type="button"
|
|
35
|
+
role="menuitemradio"
|
|
36
|
+
aria-checked={checked}
|
|
37
|
+
onClick={onSelect}
|
|
38
|
+
data-agent-switch-option="true"
|
|
39
|
+
className={cn(
|
|
40
|
+
'flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs transition-colors outline-none focus-visible:bg-muted',
|
|
41
|
+
checked ? 'bg-primary/10 font-medium text-primary' : 'text-popover-foreground hover:bg-muted',
|
|
42
|
+
)}
|
|
43
|
+
>
|
|
44
|
+
<span className="w-3.5 flex-shrink-0">{checked && <Check className="size-3.5" aria-hidden="true" />}</span>
|
|
45
|
+
<span className="truncate">{children}</span>
|
|
46
|
+
{note && <span className="ml-auto text-[10px] text-muted-foreground">{note}</span>}
|
|
47
|
+
</button>
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* OpenCode: the agent an approval hands off to, as a radio section of the
|
|
52
|
+
* decision menu (it replaces ApproveDropdown's separate caret). Reads and
|
|
53
|
+
* writes the same `agentSwitch` setting Settings does.
|
|
54
|
+
*/
|
|
55
|
+
export const AgentSwitchMenuSection: React.FC<{ agents: Agent[] }> = ({ agents }) => {
|
|
56
|
+
// Reactive: config.json's value lands through configStore.init() once
|
|
57
|
+
// /api/plan answers, which may be after this mounts (#1390).
|
|
58
|
+
const stored = useConfigValue('agentSwitch');
|
|
59
|
+
const setting: AgentSwitchSettings = stored ?? getAgentSwitchDefaults('plan');
|
|
60
|
+
const isCustom = setting.switchTo === 'custom' && !!setting.customName;
|
|
61
|
+
const title = 'After approval, switch to';
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<>
|
|
65
|
+
<ActionMenuDivider />
|
|
66
|
+
<div
|
|
67
|
+
aria-hidden="true"
|
|
68
|
+
className="px-3 pt-1.5 pb-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/70"
|
|
69
|
+
>
|
|
70
|
+
{title}
|
|
71
|
+
</div>
|
|
72
|
+
<div role="group" aria-label={title} data-agent-switch-section="true">
|
|
73
|
+
{agents.map((agent) => (
|
|
74
|
+
<RadioRow
|
|
75
|
+
key={agent.id}
|
|
76
|
+
checked={isSelected(agent.id, setting)}
|
|
77
|
+
onSelect={() => saveAgentSwitchSettings({ switchTo: agent.id })}
|
|
78
|
+
>
|
|
79
|
+
{agent.name}
|
|
80
|
+
</RadioRow>
|
|
81
|
+
))}
|
|
82
|
+
{isCustom && (
|
|
83
|
+
<RadioRow checked onSelect={() => {}} note="(custom)">
|
|
84
|
+
{setting.customName}
|
|
85
|
+
</RadioRow>
|
|
86
|
+
)}
|
|
87
|
+
<RadioRow
|
|
88
|
+
checked={setting.switchTo === 'disabled'}
|
|
89
|
+
onSelect={() => saveAgentSwitchSettings({ switchTo: 'disabled' })}
|
|
90
|
+
>
|
|
91
|
+
No switch
|
|
92
|
+
</RadioRow>
|
|
93
|
+
</div>
|
|
94
|
+
</>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Claude Code: the permission mode Approve already sends, made visible. Read
|
|
100
|
+
* only; "Change" opens Settings, where the mode is set.
|
|
101
|
+
*/
|
|
102
|
+
export const PermissionModeMenuLine: React.FC<{
|
|
103
|
+
agentName: string;
|
|
104
|
+
modeLabel: string;
|
|
105
|
+
onChange: () => void;
|
|
106
|
+
}> = ({ agentName, modeLabel, onChange }) => (
|
|
107
|
+
<>
|
|
108
|
+
<ActionMenuDivider />
|
|
109
|
+
<div
|
|
110
|
+
data-permission-mode-line="true"
|
|
111
|
+
className="flex items-center justify-between gap-2 px-3 py-1.5 text-[10px] text-muted-foreground"
|
|
112
|
+
>
|
|
113
|
+
<span>
|
|
114
|
+
After approval, {agentName} runs in{' '}
|
|
115
|
+
<span className="font-medium text-foreground">{modeLabel}</span>
|
|
116
|
+
</span>
|
|
117
|
+
<button
|
|
118
|
+
type="button"
|
|
119
|
+
role="menuitem"
|
|
120
|
+
onClick={onChange}
|
|
121
|
+
data-permission-mode-change="true"
|
|
122
|
+
aria-label={`Change the permission mode (${modeLabel}) in Settings`}
|
|
123
|
+
className="rounded px-1 py-0.5 text-[10px] text-primary outline-none hover:underline focus-visible:ring-1 focus-visible:ring-ring/60"
|
|
124
|
+
>
|
|
125
|
+
Change
|
|
126
|
+
</button>
|
|
127
|
+
</div>
|
|
128
|
+
</>
|
|
129
|
+
);
|
|
@@ -27,12 +27,14 @@ interface PlanHeaderMenuProps {
|
|
|
27
27
|
onSaveToObsidian: () => void;
|
|
28
28
|
onSaveToBear: () => void;
|
|
29
29
|
onSaveToOctarine: () => void;
|
|
30
|
+
onSaveToNotion?: () => void;
|
|
30
31
|
sharingEnabled: boolean;
|
|
31
32
|
isApiMode: boolean;
|
|
32
33
|
agentInstructionsEnabled: boolean;
|
|
33
34
|
obsidianConfigured: boolean;
|
|
34
35
|
bearConfigured: boolean;
|
|
35
36
|
octarineConfigured: boolean;
|
|
37
|
+
notionConfigured?: boolean;
|
|
36
38
|
compactTouchLayout?: boolean;
|
|
37
39
|
compactSessionActions?: CompactPlanAction[];
|
|
38
40
|
compactDocumentActions?: CompactPlanAction[];
|
|
@@ -61,12 +63,14 @@ export const PlanHeaderMenu: React.FC<PlanHeaderMenuProps> = ({
|
|
|
61
63
|
onSaveToObsidian,
|
|
62
64
|
onSaveToBear,
|
|
63
65
|
onSaveToOctarine,
|
|
66
|
+
onSaveToNotion,
|
|
64
67
|
sharingEnabled,
|
|
65
68
|
isApiMode,
|
|
66
69
|
agentInstructionsEnabled,
|
|
67
70
|
obsidianConfigured,
|
|
68
71
|
bearConfigured,
|
|
69
72
|
octarineConfigured,
|
|
73
|
+
notionConfigured = false,
|
|
70
74
|
compactTouchLayout = false,
|
|
71
75
|
compactSessionActions = [],
|
|
72
76
|
compactDocumentActions = [],
|
|
@@ -76,7 +80,7 @@ export const PlanHeaderMenu: React.FC<PlanHeaderMenuProps> = ({
|
|
|
76
80
|
const showUpdateDot = !!updateInfo?.updateAvailable && !updateInfo.dismissed;
|
|
77
81
|
|
|
78
82
|
const anyNotesAppConfigured =
|
|
79
|
-
isApiMode && (obsidianConfigured || bearConfigured || octarineConfigured);
|
|
83
|
+
isApiMode && (obsidianConfigured || bearConfigured || octarineConfigured || notionConfigured);
|
|
80
84
|
|
|
81
85
|
return (
|
|
82
86
|
<ActionMenu
|
|
@@ -263,6 +267,16 @@ export const PlanHeaderMenu: React.FC<PlanHeaderMenuProps> = ({
|
|
|
263
267
|
label="Save to Octarine"
|
|
264
268
|
/>
|
|
265
269
|
)}
|
|
270
|
+
{notionConfigured && onSaveToNotion && (
|
|
271
|
+
<ActionMenuItem
|
|
272
|
+
onClick={() => {
|
|
273
|
+
closeMenu();
|
|
274
|
+
onSaveToNotion();
|
|
275
|
+
}}
|
|
276
|
+
icon={<NoteIcon />}
|
|
277
|
+
label="Save to Notion"
|
|
278
|
+
/>
|
|
279
|
+
)}
|
|
266
280
|
</>
|
|
267
281
|
)}
|
|
268
282
|
|
|
@@ -36,6 +36,13 @@ export const OpenCodeIcon: React.FC<{ className?: string }> = ({ className = 'w-
|
|
|
36
36
|
</svg>
|
|
37
37
|
);
|
|
38
38
|
|
|
39
|
+
/** Qwen Code icon: the Qwen Code logo (QwenLM/qwen-code), in its brand purple. */
|
|
40
|
+
export const QwenIcon: React.FC<{ className?: string }> = ({ className = 'w-4 h-4' }) => (
|
|
41
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 141.38 140" className={className}>
|
|
42
|
+
<path d="m140.93 85-16.35-28.33-1.93-3.34 8.66-15a3.323 3.323 0 0 0 0-3.34l-9.62-16.67c-.3-.51-.72-.93-1.22-1.22s-1.07-.45-1.67-.45H82.23l-8.66-15a3.33 3.33 0 0 0-2.89-1.67H51.43c-.59 0-1.17.16-1.66.45-.5.29-.92.71-1.22 1.22L32.19 29.98l-1.92 3.33H12.96c-.59 0-1.17.16-1.66.45-.5.29-.93.71-1.22 1.22L.45 51.66a3.323 3.323 0 0 0 0 3.34l18.28 31.67-8.66 15a3.32 3.32 0 0 0 0 3.34l9.62 16.67c.3.51.72.93 1.22 1.22s1.07.45 1.67.45h36.56l8.66 15a3.35 3.35 0 0 0 2.89 1.67h19.25a3.34 3.34 0 0 0 2.89-1.67l18.28-31.67h17.32c.6 0 1.17-.16 1.67-.45s.92-.71 1.22-1.22l9.62-16.67a3.323 3.323 0 0 0 0-3.34ZM51.44 3.33 61.07 20l-9.63 16.66h76.98l-9.62 16.66H45.67l-11.54-20zM57.21 120H22.58l9.63-16.67h19.25l-38.5-66.67h19.25l9.62 16.67L68.78 100l-11.55 20Zm61.59-33.34-9.62-16.67-38.49 66.67-9.63-16.67 9.63-16.66 26.94-46.67h23.1l17.32 30z" fill="#6D44E8" />
|
|
43
|
+
</svg>
|
|
44
|
+
);
|
|
45
|
+
|
|
39
46
|
/** Generic fallback icon for unknown providers */
|
|
40
47
|
const GenericProviderIcon: React.FC<{ className?: string }> = ({ className = 'w-4 h-4' }) => (
|
|
41
48
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} className={className}>
|
|
@@ -56,6 +63,7 @@ export const PROVIDER_META: Record<string, { label: string; icon: React.FC<{ cla
|
|
|
56
63
|
'codex-sdk': { label: 'Codex', icon: CodexIcon },
|
|
57
64
|
'pi-sdk': { label: 'Pi', icon: PiIcon },
|
|
58
65
|
'opencode-sdk': { label: 'OpenCode', icon: OpenCodeIcon },
|
|
66
|
+
'qwen-sdk': { label: 'Qwen Code', icon: QwenIcon },
|
|
59
67
|
'session-bridge': { label: 'Ask this session', icon: SessionIcon },
|
|
60
68
|
};
|
|
61
69
|
|