@dsh-plus/llm-pi 0.1.11 → 0.1.13
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/lib/client.js +559 -345
- package/package.json +3 -2
- package/src/client/card.tsx +112 -134
- package/src/client/client.ts +5 -82
- package/src/client/fields.tsx +27 -43
- package/src/client/i18n.ts +7 -20
- package/src/client/styles.ts +27 -51
- package/src/client/views/models.tsx +4 -2
- package/src/client/views/provider-fields.tsx +4 -2
- package/src/client/views/providers.tsx +3 -1
- package/src/client/scope.ts +0 -59
package/src/client/styles.ts
CHANGED
|
@@ -1,52 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* 配置卡片样式:基础规则走 @dsh-plus/shared/client 的 cardCss('lpc'),
|
|
3
|
+
* 本文件补插件特有规则(route/model 折叠块、键值对行、网格、JSON 文本框)
|
|
4
|
+
* 及窄屏响应式:views 的双列网格在窄屏转单列、route 头部按钮换行、
|
|
5
|
+
* 键值对行纵向堆叠、文本框 16px 防 iOS 缩放。
|
|
6
|
+
* 沿用官方 data-plugin / data-plugin-css 约定(HMR 据此卸载)。
|
|
4
7
|
* @module llm-pi/client/styles
|
|
5
8
|
*/
|
|
9
|
+
import { cardCss, injectCardStyle } from '@dsh-plus/shared/client'
|
|
6
10
|
|
|
7
11
|
export const PLUGIN_ID = '@dsh-plus/llm-pi'
|
|
8
|
-
export const STYLE_TAG_ID = `${PLUGIN_ID}/card.css`
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
.lpc-
|
|
12
|
-
.lpc-
|
|
13
|
-
.lpc-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}
|
|
14
|
-
.lpc-header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}
|
|
15
|
-
.lpc-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
|
|
16
|
-
.lpc-headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
|
|
17
|
-
.lpc-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}
|
|
18
|
-
.lpc-description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}
|
|
19
|
-
.lpc-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s;font-size:12px}
|
|
20
|
-
.lpc-chevronOpen{transform:rotate(180deg)}
|
|
21
|
-
.lpc-pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}
|
|
22
|
-
.lpc-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}
|
|
23
|
-
.lpc-field{flex-direction:column;gap:6px;padding:12px 0;display:flex;min-width:0}
|
|
24
|
-
.lpc-field+.lpc-field{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
25
|
-
.lpc-head{align-items:center;gap:8px;display:flex}
|
|
26
|
-
.lpc-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}
|
|
27
|
-
.lpc-input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;width:100%;box-sizing:border-box;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px}
|
|
28
|
-
.lpc-input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
|
|
29
|
-
.lpc-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
|
|
30
|
-
.lpc-inputInvalid{border-color:var(--dsw-alias-label-error)}
|
|
31
|
-
.lpc-select{appearance:none}
|
|
13
|
+
const extraCss = `
|
|
14
|
+
.lpc-input{width:100%;box-sizing:border-box}
|
|
15
|
+
.lpc-select{appearance:none;cursor:pointer;background-image:linear-gradient(45deg,transparent 50%,var(--dsw-alias-label-secondary) 50%),linear-gradient(135deg,var(--dsw-alias-label-secondary) 50%,transparent 50%);background-position:calc(100% - 16px) 50%,calc(100% - 11px) 50%;background-size:5px 5px;background-repeat:no-repeat;padding-right:30px}
|
|
32
16
|
.lpc-textarea{height:auto;min-height:72px;padding:8px 12px;line-height:1.5;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;resize:vertical}
|
|
33
|
-
.lpc-
|
|
34
|
-
.lpc-invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}
|
|
35
|
-
.lpc-checkRow{align-items:center;gap:8px;display:flex;padding:3px 0}
|
|
36
|
-
.lpc-checkRow input{accent-color:var(--dsw-alias-brand-primary)}
|
|
37
|
-
.lpc-checkRow label{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5;cursor:pointer}
|
|
38
|
-
.lpc-groupLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;padding:14px 0 2px;margin:0}
|
|
39
|
-
.lpc-readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}
|
|
17
|
+
.lpc-checkRow{padding:3px 0}
|
|
40
18
|
.lpc-statusRow{color:var(--dsw-alias-label-tertiary);margin:6px 0 0;font-size:12px;line-height:1.6;word-break:break-all}
|
|
41
|
-
.lpc-
|
|
42
|
-
.lpc-status{min-width:0;color:var(--dsw-alias-label-secondary);flex:1;margin:0;font-size:12px;line-height:1.5}
|
|
43
|
-
.lpc-statusError{color:var(--dsw-alias-label-error)}
|
|
44
|
-
.lpc-btn{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5;flex:none}
|
|
45
|
-
.lpc-btnGhost{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}
|
|
46
|
-
.lpc-btnGhost:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}
|
|
47
|
-
.lpc-btnPrimary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}
|
|
48
|
-
.lpc-btn:disabled{opacity:.4;cursor:default}
|
|
49
|
-
.lpc-btn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
|
|
19
|
+
.lpc-btn{flex:none}
|
|
50
20
|
.lpc-btnSmall{padding:2px 10px;font-size:12px}
|
|
51
21
|
.lpc-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 16px}
|
|
52
22
|
.lpc-gridNested{margin-top:2px}
|
|
@@ -75,18 +45,24 @@ export const cardCss = `
|
|
|
75
45
|
.lpc-collapseTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600}
|
|
76
46
|
.lpc-collapseBody{border-top:1px dashed var(--dsw-alias-border-l2);padding-bottom:6px}
|
|
77
47
|
.lpc-refreshBtn{margin-left:8px;vertical-align:middle}
|
|
48
|
+
@media (max-width:767px){
|
|
49
|
+
.lpc-grid{grid-template-columns:1fr}
|
|
50
|
+
.lpc-routeHead{flex-wrap:wrap}
|
|
51
|
+
.lpc-routeKey{white-space:normal;word-break:break-all}
|
|
52
|
+
.lpc-routeBody{margin:0 8px}
|
|
53
|
+
.lpc-modelRow{padding:0 8px}
|
|
54
|
+
.lpc-modelHead{flex-wrap:wrap}
|
|
55
|
+
.lpc-kvRow{flex-direction:column;align-items:stretch}
|
|
56
|
+
.lpc-kvRow .lpc-btn{align-self:flex-end}
|
|
57
|
+
.lpc-textarea{font-size:16px}
|
|
58
|
+
.lpc-statusRow{display:flex;flex-direction:column;gap:4px}
|
|
59
|
+
.lpc-catalogBar .lpc-input{flex:1;min-width:140px}
|
|
60
|
+
}
|
|
78
61
|
`
|
|
79
62
|
|
|
63
|
+
export const cardCssAll = cardCss('lpc', extraCss)
|
|
64
|
+
|
|
80
65
|
/** 幂等注入样式标签;返回标签(已存在或环境无 document 时为 null)。 */
|
|
81
66
|
export function injectStyle(): HTMLStyleElement | null {
|
|
82
|
-
|
|
83
|
-
if (document.querySelector(`style[data-plugin-css=${JSON.stringify(STYLE_TAG_ID)}]`) !== null) {
|
|
84
|
-
return null
|
|
85
|
-
}
|
|
86
|
-
const tag = document.createElement('style')
|
|
87
|
-
tag.dataset.plugin = PLUGIN_ID
|
|
88
|
-
tag.dataset.pluginCss = STYLE_TAG_ID
|
|
89
|
-
tag.textContent = cardCss
|
|
90
|
-
document.head.appendChild(tag)
|
|
91
|
-
return tag
|
|
67
|
+
return injectCardStyle(PLUGIN_ID, cardCssAll)
|
|
92
68
|
}
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
* (builtin / models-dev)与 provider 后拉取其 models 列表。
|
|
5
5
|
* @module llm-pi/client/views/models
|
|
6
6
|
*/
|
|
7
|
-
import { type ReactElement, useEffect, useState } from 'react'
|
|
8
7
|
|
|
8
|
+
import { CheckRow } from '@dsh-plus/shared/client'
|
|
9
|
+
import { type ReactElement, useEffect, useState } from 'react'
|
|
9
10
|
import { fetchCatalog, type WireModelsDevStatus } from '../api.ts'
|
|
10
11
|
import { MODALITIES, THINKING_LEVELS } from '../constants.ts'
|
|
11
12
|
import { emptyModelDraft, type ModelDraft, type ReasoningDraft } from '../draft.ts'
|
|
12
|
-
import {
|
|
13
|
+
import { TextField } from '../fields.tsx'
|
|
13
14
|
import { CompatEditor } from './compat.tsx'
|
|
14
15
|
|
|
15
16
|
type CatalogSource = 'builtin' | 'models-dev'
|
|
@@ -227,6 +228,7 @@ export function ModelRow(props: ModelRowProps): ReactElement {
|
|
|
227
228
|
<span className="lpc-label">{t('input')}</span>
|
|
228
229
|
{MODALITIES.map((modality) => (
|
|
229
230
|
<CheckRow
|
|
231
|
+
prefix="lpc"
|
|
230
232
|
key={modality}
|
|
231
233
|
id={`${id}-input-${modality}`}
|
|
232
234
|
label={modality}
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* retryPolicy 与模型目录在 views/providers.tsx 的 route 小节内另行渲染。
|
|
5
5
|
* @module llm-pi/client/views/provider-fields
|
|
6
6
|
*/
|
|
7
|
-
import type { ReactElement } from 'react'
|
|
8
7
|
|
|
8
|
+
import { CheckRow } from '@dsh-plus/shared/client'
|
|
9
|
+
import type { ReactElement } from 'react'
|
|
9
10
|
import {
|
|
10
11
|
BUDGET_KEYS,
|
|
11
12
|
CACHE_RETENTION_OPTIONS,
|
|
@@ -15,7 +16,7 @@ import {
|
|
|
15
16
|
TRANSPORT_OPTIONS,
|
|
16
17
|
} from '../constants.ts'
|
|
17
18
|
import type { ProviderDraft } from '../draft.ts'
|
|
18
|
-
import {
|
|
19
|
+
import { SelectField, TextField } from '../fields.tsx'
|
|
19
20
|
import { pruneCompatForApi } from './compat.tsx'
|
|
20
21
|
|
|
21
22
|
export interface ProviderScalarFieldsProps {
|
|
@@ -113,6 +114,7 @@ export function ProviderScalarFields(props: ProviderScalarFieldsProps): ReactEle
|
|
|
113
114
|
<span className="lpc-label">{t('defaultInput')}</span>
|
|
114
115
|
{MODALITIES.map((modality) => (
|
|
115
116
|
<CheckRow
|
|
117
|
+
prefix="lpc"
|
|
116
118
|
key={modality}
|
|
117
119
|
id={`${props.id}-input-${modality}`}
|
|
118
120
|
label={modality}
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* 支持新增 route(输入键名)与删除 route。
|
|
5
5
|
* @module llm-pi/client/views/providers
|
|
6
6
|
*/
|
|
7
|
+
|
|
8
|
+
import { ChevronDownIcon } from '@dsh-plus/shared/client'
|
|
7
9
|
import { type ReactElement, useState } from 'react'
|
|
8
10
|
|
|
9
11
|
import type { ProviderDraft } from '../draft.ts'
|
|
@@ -111,7 +113,7 @@ export function ProviderSection(props: ProviderSectionProps): ReactElement {
|
|
|
111
113
|
aria-expanded={open}
|
|
112
114
|
onClick={() => setOpen(!open)}
|
|
113
115
|
>
|
|
114
|
-
<
|
|
116
|
+
<ChevronDownIcon className={`lpc-chevron${open ? ' lpc-chevronOpen' : ''}`} />
|
|
115
117
|
<span className="lpc-routeKey">{route}</span>
|
|
116
118
|
{summary !== '' ? <span className="lpc-routeApi">{summary}</span> : null}
|
|
117
119
|
</button>
|
package/src/client/scope.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 浏览器半 settingsScope/connection 的最小本地接口声明。
|
|
3
|
-
* 只声明本插件用到的窄面,避免为构建期类型引入整条官方 client 依赖树;
|
|
4
|
-
* 运行时契约以官方 dsh-client-connection 的 settings RPC 面为准
|
|
5
|
-
* (rc7:白名单已移除,任何已注册命名空间均可 describe/update/replace)。
|
|
6
|
-
*
|
|
7
|
-
* 实现说明:本插件不复用 settingsScope 服务的 bind()——它在非 loopback
|
|
8
|
-
* 页面(如经 tailnet 域名访问)下走 memory 模式、永远无数据;这里按官方
|
|
9
|
-
* 配置页 tab 的同款模式用 `api.settings` 直连实现 Scope(describe 读 +
|
|
10
|
-
* remote `settings/document-updated` / `connection/reset` 刷新,见 client.ts)。
|
|
11
|
-
* @module {pkg}/client/scope
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/** settings 命名空间 scope 的快照(官方 SettingsScopeSnapshot 的最小投影)。 */
|
|
15
|
-
export interface ScopeSnapshot {
|
|
16
|
-
status: 'loading' | 'ready' | 'unavailable'
|
|
17
|
-
/** schema 解析后的配置值(secret 字段已脱敏剥除)。 */
|
|
18
|
-
value: unknown
|
|
19
|
-
/** 命名空间 revision,写操作 fencing 用;首次 Host 应答前为 undefined。 */
|
|
20
|
-
revision: number | undefined
|
|
21
|
-
/** Host 文档是否可写。 */
|
|
22
|
-
writable: boolean
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** settingsScope.bind({namespace}) 返回的控制器面。 */
|
|
26
|
-
export interface Scope {
|
|
27
|
-
getSnapshot(): ScopeSnapshot
|
|
28
|
-
subscribe(listener: () => void): () => void
|
|
29
|
-
load(): Promise<void>
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** connection api.settings 的 RPC 面(本插件用到的三个方法)。 */
|
|
33
|
-
export interface SettingsApi {
|
|
34
|
-
describe(payload?: Record<string, never>): Promise<{
|
|
35
|
-
result: {
|
|
36
|
-
ok: boolean
|
|
37
|
-
value?: {
|
|
38
|
-
writable: boolean
|
|
39
|
-
namespaces: Array<{
|
|
40
|
-
ns: string
|
|
41
|
-
value: unknown
|
|
42
|
-
revision: number
|
|
43
|
-
secrets: Array<{ path: string[]; set: boolean }>
|
|
44
|
-
}>
|
|
45
|
-
}
|
|
46
|
-
error?: { message?: string }
|
|
47
|
-
}
|
|
48
|
-
}>
|
|
49
|
-
update(request: {
|
|
50
|
-
ns: string
|
|
51
|
-
patch: Record<string, unknown>
|
|
52
|
-
expectedRevision?: number
|
|
53
|
-
}): Promise<{ result: { ok: boolean; error?: { message?: string } } }>
|
|
54
|
-
replace(request: {
|
|
55
|
-
ns: string
|
|
56
|
-
section: Record<string, unknown>
|
|
57
|
-
expectedRevision?: number
|
|
58
|
-
}): Promise<{ result: { ok: boolean; error?: { message?: string } } }>
|
|
59
|
-
}
|