@nextclaw/ui 0.5.36 → 0.5.38
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/CHANGELOG.md +12 -0
- package/dist/assets/ChannelsList-3B_zyiKA.js +1 -0
- package/dist/assets/{ChatPage-B5OG3EW3.js → ChatPage-DusH09PT.js} +1 -1
- package/dist/assets/{CronConfig-MXdvM9gu.js → CronConfig-5GTz5wPt.js} +1 -1
- package/dist/assets/DocBrowser-BtqGmg0N.js +1 -0
- package/dist/assets/MarketplacePage-BEW4M9BT.js +49 -0
- package/dist/assets/{ModelConfig-qJyJ1XS-.js → ModelConfig-CwxXYqME.js} +1 -1
- package/dist/assets/ProvidersList-D4oaYHpJ.js +1 -0
- package/dist/assets/{RuntimeConfig-DoKy3o8n.js → RuntimeConfig-BwTxGi_U.js} +1 -1
- package/dist/assets/{SecretsConfig-S_jppujG.js → SecretsConfig-x36MY4ym.js} +1 -1
- package/dist/assets/{SessionsConfig-C5VnCiw_.js → SessionsConfig-qEffYDZ0.js} +1 -1
- package/dist/assets/{card-Bsb-eVmY.js → card-Bq6uwDJQ.js} +1 -1
- package/dist/assets/index-DMEuanmd.css +1 -0
- package/dist/assets/index-wB2uPrKu.js +2 -0
- package/dist/assets/{label-CXP5KktX.js → label-Cq1vSfWg.js} +1 -1
- package/dist/assets/logos-BKBMs40Q.js +1 -0
- package/dist/assets/{page-layout-DMWzimj9.js → page-layout-D8MW2vP-.js} +1 -1
- package/dist/assets/{switch-oEZ0AFmj.js → switch-CycMxy31.js} +1 -1
- package/dist/assets/{tabs-custom-CQP93tp3.js → tabs-custom-N4olWJSw.js} +1 -1
- package/dist/assets/{useConfig-C0nxJgik.js → useConfig-tR_KAfMV.js} +1 -1
- package/dist/assets/{useConfirmDialog-d6TTs8io.js → useConfirmDialog-DE0Yp8Ai.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/api/marketplace.ts +24 -0
- package/src/api/types.ts +28 -0
- package/src/components/config/ChannelForm.tsx +22 -19
- package/src/components/config/ChannelsList.tsx +7 -6
- package/src/components/config/ModelConfig.tsx +1 -0
- package/src/components/config/ProviderForm.tsx +4 -2
- package/src/components/config/ProvidersList.tsx +3 -2
- package/src/components/config/config-layout.ts +10 -0
- package/src/components/doc-browser/DocBrowser.tsx +382 -323
- package/src/components/doc-browser/DocBrowserContext.tsx +389 -157
- package/src/components/layout/Sidebar.tsx +1 -1
- package/src/components/marketplace/MarketplacePage.tsx +252 -12
- package/src/lib/i18n.ts +21 -2
- package/dist/assets/ChannelsList-BTQcN7OQ.js +0 -1
- package/dist/assets/DocBrowser-CJDon901.js +0 -1
- package/dist/assets/MarketplacePage-BwaTwPfP.js +0 -1
- package/dist/assets/ProvidersList-DinfLIyS.js +0 -1
- package/dist/assets/index-BzQBLXUW.js +0 -2
- package/dist/assets/index-DcyOd66N.css +0 -1
- package/dist/assets/logos-DqE_6ErA.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as e}from"./vendor-DN_iJQc4.js";import{f as m,c as s}from"./index-
|
|
1
|
+
import{j as e}from"./vendor-DN_iJQc4.js";import{f as m,c as s}from"./index-wB2uPrKu.js";function c({tabs:a,activeTab:i,onChange:o,className:n}){return e.jsx("div",{className:s("flex items-center gap-6 border-b border-gray-200/60 mb-6",n),children:a.map(t=>{const r=i===t.id;return e.jsxs("button",{onClick:()=>o(t.id),className:s("relative pb-3 text-[14px] font-medium transition-all duration-fast flex items-center gap-1.5",r?"text-gray-900":"text-gray-600 hover:text-gray-900"),children:[t.label,t.count!==void 0&&e.jsx("span",{className:s("text-[11px] font-medium","text-gray-500"),children:m(t.count)}),r&&e.jsx("div",{className:"absolute bottom-0 left-0 right-0 h-[2px] bg-primary rounded-full"})]},t.id)})})}export{c as T};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r as C,j as F,aq as m,ar as f,as as u,ag as s}from"./vendor-DN_iJQc4.js";import{c as b,A as x,j as i,t as o}from"./index-
|
|
1
|
+
import{r as C,j as F,aq as m,ar as f,as as u,ag as s}from"./vendor-DN_iJQc4.js";import{c as b,A as x,j as i,t as o}from"./index-wB2uPrKu.js";const K=C.forwardRef(({className:n,type:e,...r},a)=>F.jsx("input",{type:e,className:b("flex h-9 w-full rounded-xl border border-gray-200/80 bg-white px-3.5 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-primary/40 focus:border-primary/40 transition-colors disabled:cursor-not-allowed disabled:opacity-50",n),ref:a,...r}));K.displayName="Input";async function k(){const n=await i.get("/api/config");if(!n.ok)throw new Error(n.error.message);return n.data}async function A(){const n=await i.get("/api/config/meta");if(!n.ok)throw new Error(n.error.message);return n.data}async function M(){const n=await i.get("/api/config/schema");if(!n.ok)throw new Error(n.error.message);return n.data}async function R(n){const e=await i.put("/api/config/model",n);if(!e.ok)throw new Error(e.error.message);return e.data}async function J(n,e){const r=await i.put(`/api/config/providers/${n}`,e);if(!r.ok)throw new Error(r.error.message);return r.data}async function Q(n,e){const r=await i.post(`/api/config/providers/${n}/test`,e);if(!r.ok)throw new Error(r.error.message);return r.data}async function U(n,e){const r=await i.put(`/api/config/channels/${n}`,e);if(!r.ok)throw new Error(r.error.message);return r.data}async function $(n){const e=await i.put("/api/config/runtime",n);if(!e.ok)throw new Error(e.error.message);return e.data}async function T(n){const e=await i.put("/api/config/secrets",n);if(!e.ok)throw new Error(e.error.message);return e.data}async function I(n,e){const r=await i.post(`/api/config/actions/${n}/execute`,e);if(!r.ok)throw new Error(r.error.message);return r.data}async function P(n){var d;const e=new URLSearchParams;(d=n==null?void 0:n.q)!=null&&d.trim()&&e.set("q",n.q.trim()),typeof(n==null?void 0:n.limit)=="number"&&Number.isFinite(n.limit)&&e.set("limit",String(Math.max(0,Math.trunc(n.limit)))),typeof(n==null?void 0:n.activeMinutes)=="number"&&Number.isFinite(n.activeMinutes)&&e.set("activeMinutes",String(Math.max(0,Math.trunc(n.activeMinutes))));const r=e.toString(),a=await i.get(r?"/api/sessions?"+r:"/api/sessions");if(!a.ok)throw new Error(a.error.message);return a.data}async function N(n,e=200){const r=await i.get(`/api/sessions/${encodeURIComponent(n)}/history?limit=${Math.max(1,Math.trunc(e))}`);if(!r.ok)throw new Error(r.error.message);return r.data}async function O(n,e){const r=await i.put(`/api/sessions/${encodeURIComponent(n)}`,e);if(!r.ok)throw new Error(r.error.message);return r.data}async function j(n){const e=await i.delete(`/api/sessions/${encodeURIComponent(n)}`);if(!e.ok)throw new Error(e.error.message);return e.data}function D(n){const e=n.split(/\r?\n/);let r="message";const a=[];for(const d of e){if(d.startsWith("event:")){r=d.slice(6).trim()||"message";continue}d.startsWith("data:")&&a.push(d.slice(5).trimStart())}return a.length===0?null:{event:r,data:a.join(`
|
|
2
2
|
`)}}async function z(n,e={}){var q;const r=await fetch(`${x}/api/chat/turn/stream`,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n),signal:e.signal});if(!r.ok){let g=`chat stream failed (${r.status} ${r.statusText})`;try{const t=await r.json();(q=t==null?void 0:t.error)!=null&&q.message&&(g=t.error.message)}catch{const t=await r.text().catch(()=>"");t.trim()&&(g=t.trim())}throw new Error(g)}if(!r.body)throw new Error("chat stream is not readable");const a=r.body.getReader(),d=new TextDecoder;let l="",w=null;const p=g=>{var y,h,E;const t=D(g);if(t){if(t.event==="ready"){try{const c=JSON.parse(t.data);(y=e.onReady)==null||y.call(e,{event:"ready",sessionKey:String(c.sessionKey??""),requestedAt:String(c.requestedAt??"")})}catch{}return}if(t.event==="delta"){try{const c=JSON.parse(t.data);typeof c.delta=="string"&&c.delta.length>0&&((h=e.onDelta)==null||h.call(e,{event:"delta",delta:c.delta}))}catch{}return}if(t.event==="session_event"){try{(E=e.onSessionEvent)==null||E.call(e,{event:"session_event",data:JSON.parse(t.data)})}catch{}return}if(t.event==="final"){w=JSON.parse(t.data);return}if(t.event==="error")try{const c=JSON.parse(t.data);throw new Error(typeof c.message=="string"&&c.message?c.message:"chat stream failed")}catch(c){throw c instanceof Error?c:new Error("chat stream failed")}}};let S=!1;for(;!S;){const{done:g,value:t}=await a.read();if(g){S=!0;continue}l+=d.decode(t,{stream:!0});let y=l.indexOf(`
|
|
3
3
|
|
|
4
4
|
`);for(;y!==-1;){const h=l.slice(0,y).trim();l=l.slice(y+2),h&&p(h),y=l.indexOf(`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r as i,aw as b,aL as w,au as ie,j as s,at as A,aB as le,ay as h,az as D,aC as ce,aM as de,aN as ue,aO as fe,aP as ge,aD as me,aQ as pe,aR as ve,ad as xe}from"./vendor-DN_iJQc4.js";import{c as N,t as x}from"./index-
|
|
1
|
+
import{r as i,aw as b,aL as w,au as ie,j as s,at as A,aB as le,ay as h,az as D,aC as ce,aM as de,aN as ue,aO as fe,aP as ge,aD as me,aQ as pe,aR as ve,ad as xe}from"./vendor-DN_iJQc4.js";import{c as N,t as x}from"./index-wB2uPrKu.js";import{B as I}from"./page-layout-D8MW2vP-.js";function Ne(e,t){return i.useReducer((o,a)=>t[o][a]??o,e)}var E=e=>{const{present:t,children:o}=e,a=De(t),r=typeof o=="function"?o({present:a.isPresent}):i.Children.only(o),n=b(a.ref,he(r));return typeof o=="function"||a.isPresent?i.cloneElement(r,{ref:n}):null};E.displayName="Presence";function De(e){const[t,o]=i.useState(),a=i.useRef(null),r=i.useRef(e),n=i.useRef("none"),l=e?"mounted":"unmounted",[c,u]=Ne(l,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return i.useEffect(()=>{const d=y(a.current);n.current=c==="mounted"?d:"none"},[c]),w(()=>{const d=a.current,f=r.current;if(f!==e){const C=n.current,p=y(d);e?u("MOUNT"):p==="none"||(d==null?void 0:d.display)==="none"?u("UNMOUNT"):u(f&&C!==p?"ANIMATION_OUT":"UNMOUNT"),r.current=e}},[e,u]),w(()=>{if(t){let d;const f=t.ownerDocument.defaultView??window,m=p=>{const re=y(a.current).includes(CSS.escape(p.animationName));if(p.target===t&&re&&(u("ANIMATION_END"),!r.current)){const se=t.style.animationFillMode;t.style.animationFillMode="forwards",d=f.setTimeout(()=>{t.style.animationFillMode==="forwards"&&(t.style.animationFillMode=se)})}},C=p=>{p.target===t&&(n.current=y(a.current))};return t.addEventListener("animationstart",C),t.addEventListener("animationcancel",m),t.addEventListener("animationend",m),()=>{f.clearTimeout(d),t.removeEventListener("animationstart",C),t.removeEventListener("animationcancel",m),t.removeEventListener("animationend",m)}}else u("ANIMATION_END")},[t,u]),{isPresent:["mounted","unmountSuspended"].includes(c),ref:i.useCallback(d=>{a.current=d?getComputedStyle(d):null,o(d)},[])}}function y(e){return(e==null?void 0:e.animationName)||"none"}function he(e){var a,r;let t=(a=Object.getOwnPropertyDescriptor(e.props,"ref"))==null?void 0:a.get,o=t&&"isReactWarning"in t&&t.isReactWarning;return o?e.ref:(t=(r=Object.getOwnPropertyDescriptor(e,"ref"))==null?void 0:r.get,o=t&&"isReactWarning"in t&&t.isReactWarning,o?e.props.ref:e.props.ref||e.ref)}var O="Dialog",[M]=ce(O),[Ce,g]=M(O),T=e=>{const{__scopeDialog:t,children:o,open:a,defaultOpen:r,onOpenChange:n,modal:l=!0}=e,c=i.useRef(null),u=i.useRef(null),[d,f]=ie({prop:a,defaultProp:r??!1,onChange:n,caller:O});return s.jsx(Ce,{scope:t,triggerRef:c,contentRef:u,contentId:A(),titleId:A(),descriptionId:A(),open:d,onOpenChange:f,onOpenToggle:i.useCallback(()=>f(m=>!m),[f]),modal:l,children:o})};T.displayName=O;var S="DialogTrigger",ye=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(S,o),n=b(t,r.triggerRef);return s.jsx(h.button,{type:"button","aria-haspopup":"dialog","aria-expanded":r.open,"aria-controls":r.contentId,"data-state":_(r.open),...a,ref:n,onClick:D(e.onClick,r.onOpenToggle)})});ye.displayName=S;var j="DialogPortal",[Re,F]=M(j,{forceMount:void 0}),L=e=>{const{__scopeDialog:t,forceMount:o,children:a,container:r}=e,n=g(j,t);return s.jsx(Re,{scope:t,forceMount:o,children:i.Children.map(a,l=>s.jsx(E,{present:o||n.open,children:s.jsx(le,{asChild:!0,container:r,children:l})}))})};L.displayName=j;var R="DialogOverlay",W=i.forwardRef((e,t)=>{const o=F(R,e.__scopeDialog),{forceMount:a=o.forceMount,...r}=e,n=g(R,e.__scopeDialog);return n.modal?s.jsx(E,{present:a||n.open,children:s.jsx(Ee,{...r,ref:t})}):null});W.displayName=R;var be=pe("DialogOverlay.RemoveScroll"),Ee=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(R,o);return s.jsx(ue,{as:be,allowPinchZoom:!0,shards:[r.contentRef],children:s.jsx(h.div,{"data-state":_(r.open),...a,ref:t,style:{pointerEvents:"auto",...a.style}})})}),v="DialogContent",k=i.forwardRef((e,t)=>{const o=F(v,e.__scopeDialog),{forceMount:a=o.forceMount,...r}=e,n=g(v,e.__scopeDialog);return s.jsx(E,{present:a||n.open,children:n.modal?s.jsx(Oe,{...r,ref:t}):s.jsx(Ae,{...r,ref:t})})});k.displayName=v;var Oe=i.forwardRef((e,t)=>{const o=g(v,e.__scopeDialog),a=i.useRef(null),r=b(t,o.contentRef,a);return i.useEffect(()=>{const n=a.current;if(n)return de(n)},[]),s.jsx(U,{...e,ref:r,trapFocus:o.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:D(e.onCloseAutoFocus,n=>{var l;n.preventDefault(),(l=o.triggerRef.current)==null||l.focus()}),onPointerDownOutside:D(e.onPointerDownOutside,n=>{const l=n.detail.originalEvent,c=l.button===0&&l.ctrlKey===!0;(l.button===2||c)&&n.preventDefault()}),onFocusOutside:D(e.onFocusOutside,n=>n.preventDefault())})}),Ae=i.forwardRef((e,t)=>{const o=g(v,e.__scopeDialog),a=i.useRef(!1),r=i.useRef(!1);return s.jsx(U,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:n=>{var l,c;(l=e.onCloseAutoFocus)==null||l.call(e,n),n.defaultPrevented||(a.current||(c=o.triggerRef.current)==null||c.focus(),n.preventDefault()),a.current=!1,r.current=!1},onInteractOutside:n=>{var u,d;(u=e.onInteractOutside)==null||u.call(e,n),n.defaultPrevented||(a.current=!0,n.detail.originalEvent.type==="pointerdown"&&(r.current=!0));const l=n.target;((d=o.triggerRef.current)==null?void 0:d.contains(l))&&n.preventDefault(),n.detail.originalEvent.type==="focusin"&&r.current&&n.preventDefault()}})}),U=i.forwardRef((e,t)=>{const{__scopeDialog:o,trapFocus:a,onOpenAutoFocus:r,onCloseAutoFocus:n,...l}=e,c=g(v,o),u=i.useRef(null),d=b(t,u);return fe(),s.jsxs(s.Fragment,{children:[s.jsx(ge,{asChild:!0,loop:!0,trapped:a,onMountAutoFocus:r,onUnmountAutoFocus:n,children:s.jsx(me,{role:"dialog",id:c.contentId,"aria-describedby":c.descriptionId,"aria-labelledby":c.titleId,"data-state":_(c.open),...l,ref:d,onDismiss:()=>c.onOpenChange(!1)})}),s.jsxs(s.Fragment,{children:[s.jsx(je,{titleId:c.titleId}),s.jsx(_e,{contentRef:u,descriptionId:c.descriptionId})]})]})}),P="DialogTitle",$=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(P,o);return s.jsx(h.h2,{id:r.titleId,...a,ref:t})});$.displayName=P;var G="DialogDescription",B=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(G,o);return s.jsx(h.p,{id:r.descriptionId,...a,ref:t})});B.displayName=G;var z="DialogClose",H=i.forwardRef((e,t)=>{const{__scopeDialog:o,...a}=e,r=g(z,o);return s.jsx(h.button,{type:"button",...a,ref:t,onClick:D(e.onClick,()=>r.onOpenChange(!1))})});H.displayName=z;function _(e){return e?"open":"closed"}var V="DialogTitleWarning",[$e,q]=ve(V,{contentName:v,titleName:P,docsSlug:"dialog"}),je=({titleId:e})=>{const t=q(V),o=`\`${t.contentName}\` requires a \`${t.titleName}\` for the component to be accessible for screen reader users.
|
|
2
2
|
|
|
3
3
|
If you want to hide the \`${t.titleName}\`, you can wrap it with our VisuallyHidden component.
|
|
4
4
|
|
package/dist/index.html
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>NextClaw - 系统配置</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-wB2uPrKu.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendor-DN_iJQc4.js">
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DMEuanmd.css">
|
|
12
12
|
</head>
|
|
13
13
|
|
|
14
14
|
<body>
|
package/package.json
CHANGED
package/src/api/marketplace.ts
CHANGED
|
@@ -5,7 +5,9 @@ import type {
|
|
|
5
5
|
MarketplaceManageRequest,
|
|
6
6
|
MarketplaceManageResult,
|
|
7
7
|
MarketplaceInstalledView,
|
|
8
|
+
MarketplacePluginContentView,
|
|
8
9
|
MarketplaceItemType,
|
|
10
|
+
MarketplaceSkillContentView,
|
|
9
11
|
MarketplaceItemView,
|
|
10
12
|
MarketplaceListView,
|
|
11
13
|
MarketplaceRecommendationView,
|
|
@@ -68,6 +70,28 @@ export async function fetchMarketplaceItem(slug: string, type: MarketplaceItemTy
|
|
|
68
70
|
return response.data;
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
export async function fetchMarketplaceSkillContent(slug: string): Promise<MarketplaceSkillContentView> {
|
|
74
|
+
const response = await api.get<MarketplaceSkillContentView>(
|
|
75
|
+
`/api/marketplace/skills/items/${encodeURIComponent(slug)}/content`
|
|
76
|
+
);
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
throw new Error(response.error.message);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return response.data;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function fetchMarketplacePluginContent(slug: string): Promise<MarketplacePluginContentView> {
|
|
85
|
+
const response = await api.get<MarketplacePluginContentView>(
|
|
86
|
+
`/api/marketplace/plugins/items/${encodeURIComponent(slug)}/content`
|
|
87
|
+
);
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
throw new Error(response.error.message);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return response.data;
|
|
93
|
+
}
|
|
94
|
+
|
|
71
95
|
export async function fetchMarketplaceRecommendations(
|
|
72
96
|
type: MarketplaceItemType,
|
|
73
97
|
params: {
|
package/src/api/types.ts
CHANGED
|
@@ -446,12 +446,15 @@ export type MarketplaceInstallSpec = {
|
|
|
446
446
|
command: string;
|
|
447
447
|
};
|
|
448
448
|
|
|
449
|
+
export type MarketplaceLocalizedTextMap = Record<string, string>;
|
|
450
|
+
|
|
449
451
|
export type MarketplaceItemSummary = {
|
|
450
452
|
id: string;
|
|
451
453
|
slug: string;
|
|
452
454
|
type: MarketplaceItemType;
|
|
453
455
|
name: string;
|
|
454
456
|
summary: string;
|
|
457
|
+
summaryI18n: MarketplaceLocalizedTextMap;
|
|
455
458
|
tags: string[];
|
|
456
459
|
author: string;
|
|
457
460
|
install: MarketplaceInstallSpec;
|
|
@@ -460,11 +463,36 @@ export type MarketplaceItemSummary = {
|
|
|
460
463
|
|
|
461
464
|
export type MarketplaceItemView = MarketplaceItemSummary & {
|
|
462
465
|
description?: string;
|
|
466
|
+
descriptionI18n?: MarketplaceLocalizedTextMap;
|
|
463
467
|
sourceRepo?: string;
|
|
464
468
|
homepage?: string;
|
|
465
469
|
publishedAt: string;
|
|
466
470
|
};
|
|
467
471
|
|
|
472
|
+
export type MarketplaceSkillContentView = {
|
|
473
|
+
type: 'skill';
|
|
474
|
+
slug: string;
|
|
475
|
+
name: string;
|
|
476
|
+
install: MarketplaceInstallSpec;
|
|
477
|
+
source: 'workspace' | 'builtin' | 'git' | 'remote';
|
|
478
|
+
raw: string;
|
|
479
|
+
metadataRaw?: string;
|
|
480
|
+
bodyRaw: string;
|
|
481
|
+
sourceUrl?: string;
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
export type MarketplacePluginContentView = {
|
|
485
|
+
type: 'plugin';
|
|
486
|
+
slug: string;
|
|
487
|
+
name: string;
|
|
488
|
+
install: MarketplaceInstallSpec;
|
|
489
|
+
source: 'npm' | 'repo' | 'remote';
|
|
490
|
+
raw?: string;
|
|
491
|
+
bodyRaw?: string;
|
|
492
|
+
metadataRaw?: string;
|
|
493
|
+
sourceUrl?: string;
|
|
494
|
+
};
|
|
495
|
+
|
|
468
496
|
export type MarketplaceListView = {
|
|
469
497
|
total: number;
|
|
470
498
|
page: number;
|
|
@@ -16,6 +16,7 @@ import { Settings, ToggleLeft, Hash, Mail, Globe, KeyRound, BookOpen } from 'luc
|
|
|
16
16
|
import type { ConfigActionManifest } from '@/api/types';
|
|
17
17
|
import { resolveChannelTutorialUrl } from '@/lib/channel-tutorials';
|
|
18
18
|
import { getChannelLogo } from '@/lib/logos';
|
|
19
|
+
import { CONFIG_DETAIL_CARD_CLASS, CONFIG_EMPTY_DETAIL_CARD_CLASS } from './config-layout';
|
|
19
20
|
|
|
20
21
|
type ChannelFieldType = 'boolean' | 'text' | 'email' | 'password' | 'number' | 'tags' | 'select' | 'json';
|
|
21
22
|
type ChannelOption = { value: string; label: string };
|
|
@@ -325,7 +326,7 @@ export function ChannelForm({ channelName }: ChannelFormProps) {
|
|
|
325
326
|
|
|
326
327
|
if (!channelName || !channelMeta || !channelConfig) {
|
|
327
328
|
return (
|
|
328
|
-
<div className=
|
|
329
|
+
<div className={CONFIG_EMPTY_DETAIL_CARD_CLASS}>
|
|
329
330
|
<div>
|
|
330
331
|
<h3 className="text-base font-semibold text-gray-900">{t('channelsSelectTitle')}</h3>
|
|
331
332
|
<p className="mt-2 text-sm text-gray-500">{t('channelsSelectDescription')}</p>
|
|
@@ -337,9 +338,9 @@ export function ChannelForm({ channelName }: ChannelFormProps) {
|
|
|
337
338
|
const enabled = Boolean(channelConfig.enabled);
|
|
338
339
|
|
|
339
340
|
return (
|
|
340
|
-
<div className=
|
|
341
|
+
<div className={CONFIG_DETAIL_CARD_CLASS}>
|
|
341
342
|
<div className="border-b border-gray-100 px-6 py-5">
|
|
342
|
-
<div className="flex flex-wrap items-
|
|
343
|
+
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
343
344
|
<div className="min-w-0">
|
|
344
345
|
<div className="flex items-center gap-3">
|
|
345
346
|
<LogoBadge
|
|
@@ -370,7 +371,7 @@ export function ChannelForm({ channelName }: ChannelFormProps) {
|
|
|
370
371
|
</div>
|
|
371
372
|
|
|
372
373
|
<form onSubmit={handleSubmit} className="flex min-h-0 flex-1 flex-col">
|
|
373
|
-
<div className="min-h-0 flex-1 space-y-
|
|
374
|
+
<div className="min-h-0 flex-1 space-y-6 overflow-y-auto px-6 py-5">
|
|
374
375
|
{fields.map((field) => {
|
|
375
376
|
const hint = channelName
|
|
376
377
|
? hintForPath(`channels.${channelName}.${field.name}`, uiHints)
|
|
@@ -470,7 +471,7 @@ export function ChannelForm({ channelName }: ChannelFormProps) {
|
|
|
470
471
|
[field.name]: event.target.value
|
|
471
472
|
}))
|
|
472
473
|
}
|
|
473
|
-
className="min-h-[120px] w-full rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs font-mono"
|
|
474
|
+
className="min-h-[120px] w-full resize-none rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs font-mono"
|
|
474
475
|
/>
|
|
475
476
|
)}
|
|
476
477
|
</div>
|
|
@@ -478,20 +479,22 @@ export function ChannelForm({ channelName }: ChannelFormProps) {
|
|
|
478
479
|
})}
|
|
479
480
|
</div>
|
|
480
481
|
|
|
481
|
-
<div className="flex flex-wrap items-center justify-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
482
|
+
<div className="flex flex-wrap items-center justify-between gap-3 border-t border-gray-100 px-6 py-4">
|
|
483
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
484
|
+
{actions
|
|
485
|
+
.filter((action) => action.trigger === 'manual')
|
|
486
|
+
.map((action) => (
|
|
487
|
+
<Button
|
|
488
|
+
key={action.id}
|
|
489
|
+
type="button"
|
|
490
|
+
onClick={() => handleManualAction(action)}
|
|
491
|
+
disabled={updateChannel.isPending || Boolean(runningActionId)}
|
|
492
|
+
variant="secondary"
|
|
493
|
+
>
|
|
494
|
+
{runningActionId === action.id ? t('connecting') : action.title}
|
|
495
|
+
</Button>
|
|
496
|
+
))}
|
|
497
|
+
</div>
|
|
495
498
|
<Button type="submit" disabled={updateChannel.isPending || Boolean(runningActionId)}>
|
|
496
499
|
{updateChannel.isPending ? t('saving') : t('save')}
|
|
497
500
|
</Button>
|
|
@@ -12,6 +12,7 @@ import { t } from '@/lib/i18n';
|
|
|
12
12
|
import { PageLayout, PageHeader } from '@/components/layout/page-layout';
|
|
13
13
|
import { resolveChannelTutorialUrl } from '@/lib/channel-tutorials';
|
|
14
14
|
import { Input } from '@/components/ui/input';
|
|
15
|
+
import { CONFIG_SIDEBAR_CARD_CLASS, CONFIG_SPLIT_GRID_CLASS } from './config-layout';
|
|
15
16
|
|
|
16
17
|
const channelDescriptionKeys: Record<string, string> = {
|
|
17
18
|
telegram: 'channelDescTelegram',
|
|
@@ -30,17 +31,17 @@ export function ChannelsList() {
|
|
|
30
31
|
const [selectedChannel, setSelectedChannel] = useState<string | undefined>();
|
|
31
32
|
const [query, setQuery] = useState('');
|
|
32
33
|
const uiHints = schema?.uiHints;
|
|
33
|
-
const channels = meta?.channels
|
|
34
|
+
const channels = meta?.channels;
|
|
34
35
|
const channelConfigs = config?.channels;
|
|
35
36
|
|
|
36
37
|
const tabs = [
|
|
37
|
-
{ id: 'enabled', label: t('channelsTabEnabled'), count: channels.filter((c) => channelConfigs?.[c.name]?.enabled).length },
|
|
38
|
-
{ id: 'all', label: t('channelsTabAll'), count: channels.length }
|
|
38
|
+
{ id: 'enabled', label: t('channelsTabEnabled'), count: (channels ?? []).filter((c) => channelConfigs?.[c.name]?.enabled).length },
|
|
39
|
+
{ id: 'all', label: t('channelsTabAll'), count: (channels ?? []).length }
|
|
39
40
|
];
|
|
40
41
|
|
|
41
42
|
const filteredChannels = useMemo(() => {
|
|
42
43
|
const keyword = query.trim().toLowerCase();
|
|
43
|
-
return channels
|
|
44
|
+
return (channels ?? [])
|
|
44
45
|
.filter((channel) => {
|
|
45
46
|
const enabled = channelConfigs?.[channel.name]?.enabled || false;
|
|
46
47
|
if (activeTab === 'enabled') {
|
|
@@ -76,8 +77,8 @@ export function ChannelsList() {
|
|
|
76
77
|
<PageLayout>
|
|
77
78
|
<PageHeader title={t('channelsPageTitle')} description={t('channelsPageDescription')} />
|
|
78
79
|
|
|
79
|
-
<div className=
|
|
80
|
-
<section className=
|
|
80
|
+
<div className={CONFIG_SPLIT_GRID_CLASS}>
|
|
81
|
+
<section className={CONFIG_SIDEBAR_CARD_CLASS}>
|
|
81
82
|
<div className="border-b border-gray-100 px-4 pt-4">
|
|
82
83
|
<Tabs tabs={tabs} activeTab={activeTab} onChange={setActiveTab} className="mb-0" />
|
|
83
84
|
</div>
|
|
@@ -259,6 +259,7 @@ export function ModelConfig() {
|
|
|
259
259
|
/>
|
|
260
260
|
</div>
|
|
261
261
|
<p className="text-xs text-gray-400">{modelHelpText}</p>
|
|
262
|
+
<p className="text-xs text-gray-500">{t('modelInputCustomHint')}</p>
|
|
262
263
|
<a
|
|
263
264
|
href={`${DOCS_DEFAULT_BASE_URL}/guide/model-selection`}
|
|
264
265
|
className="inline-flex items-center gap-1.5 text-xs text-primary hover:text-primary-hover transition-colors"
|
|
@@ -12,6 +12,7 @@ import { hintForPath } from '@/lib/config-hints';
|
|
|
12
12
|
import type { ProviderConfigUpdate, ProviderConnectionTestRequest } from '@/api/types';
|
|
13
13
|
import { KeyRound, Globe, Hash, RotateCcw, CircleDotDashed, Sparkles, Plus, X } from 'lucide-react';
|
|
14
14
|
import { toast } from 'sonner';
|
|
15
|
+
import { CONFIG_DETAIL_CARD_CLASS, CONFIG_EMPTY_DETAIL_CARD_CLASS } from './config-layout';
|
|
15
16
|
|
|
16
17
|
type WireApiType = 'auto' | 'chat' | 'responses';
|
|
17
18
|
|
|
@@ -319,7 +320,7 @@ export function ProviderForm({ providerName }: ProviderFormProps) {
|
|
|
319
320
|
|
|
320
321
|
if (!providerName || !providerSpec || !providerConfig) {
|
|
321
322
|
return (
|
|
322
|
-
<div className=
|
|
323
|
+
<div className={CONFIG_EMPTY_DETAIL_CARD_CLASS}>
|
|
323
324
|
<div>
|
|
324
325
|
<h3 className="text-base font-semibold text-gray-900">{t('providersSelectTitle')}</h3>
|
|
325
326
|
<p className="mt-2 text-sm text-gray-500">{t('providersSelectDescription')}</p>
|
|
@@ -331,7 +332,7 @@ export function ProviderForm({ providerName }: ProviderFormProps) {
|
|
|
331
332
|
const statusLabel = providerConfig.apiKeySet ? t('statusReady') : t('statusSetup');
|
|
332
333
|
|
|
333
334
|
return (
|
|
334
|
-
<div className=
|
|
335
|
+
<div className={CONFIG_DETAIL_CARD_CLASS}>
|
|
335
336
|
<div className="border-b border-gray-100 px-6 py-5">
|
|
336
337
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
337
338
|
<div className="min-w-0">
|
|
@@ -431,6 +432,7 @@ export function ProviderForm({ providerName }: ProviderFormProps) {
|
|
|
431
432
|
{t('providerAddModel')}
|
|
432
433
|
</Button>
|
|
433
434
|
</div>
|
|
435
|
+
<p className="text-xs text-gray-500">{t('providerModelInputHint')}</p>
|
|
434
436
|
|
|
435
437
|
{models.length === 0 ? (
|
|
436
438
|
<div className="rounded-xl border border-dashed border-gray-200 bg-gray-50 px-3 py-2 text-xs text-gray-500">
|
|
@@ -11,6 +11,7 @@ import { StatusDot } from '@/components/ui/status-dot';
|
|
|
11
11
|
import { t } from '@/lib/i18n';
|
|
12
12
|
import { PageLayout, PageHeader } from '@/components/layout/page-layout';
|
|
13
13
|
import { Input } from '@/components/ui/input';
|
|
14
|
+
import { CONFIG_SIDEBAR_CARD_CLASS, CONFIG_SPLIT_GRID_CLASS } from './config-layout';
|
|
14
15
|
|
|
15
16
|
function formatBasePreview(base?: string | null): string | null {
|
|
16
17
|
if (!base) {
|
|
@@ -86,8 +87,8 @@ export function ProvidersList() {
|
|
|
86
87
|
<PageLayout>
|
|
87
88
|
<PageHeader title={t('providersPageTitle')} description={t('providersPageDescription')} />
|
|
88
89
|
|
|
89
|
-
<div className=
|
|
90
|
-
<section className=
|
|
90
|
+
<div className={CONFIG_SPLIT_GRID_CLASS}>
|
|
91
|
+
<section className={CONFIG_SIDEBAR_CARD_CLASS}>
|
|
91
92
|
<div className="border-b border-gray-100 px-4 pt-4">
|
|
92
93
|
<Tabs tabs={tabs} activeTab={activeTab} onChange={setActiveTab} className="mb-0" />
|
|
93
94
|
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const CONFIG_SPLIT_GRID_CLASS = 'grid min-h-0 grid-cols-1 gap-5 xl:grid-cols-[340px_minmax(0,1fr)]';
|
|
2
|
+
|
|
3
|
+
export const CONFIG_SIDEBAR_CARD_CLASS =
|
|
4
|
+
'flex min-h-[520px] min-h-0 min-w-0 flex-col overflow-hidden rounded-2xl border border-gray-200/70 bg-white shadow-card xl:h-[calc(100vh-180px)] xl:min-h-[600px] xl:max-h-[860px]';
|
|
5
|
+
|
|
6
|
+
export const CONFIG_DETAIL_CARD_CLASS =
|
|
7
|
+
'flex min-h-[520px] min-h-0 min-w-0 flex-col overflow-hidden rounded-2xl border border-gray-200/70 bg-white shadow-card xl:h-[calc(100vh-180px)] xl:min-h-[600px] xl:max-h-[860px]';
|
|
8
|
+
|
|
9
|
+
export const CONFIG_EMPTY_DETAIL_CARD_CLASS =
|
|
10
|
+
'flex min-h-[520px] min-w-0 items-center justify-center overflow-hidden rounded-2xl border border-gray-200/70 bg-white px-6 text-center xl:h-[calc(100vh-180px)] xl:min-h-[600px] xl:max-h-[860px]';
|