@dungsil/dsh-plugin-ui-model-picker 0.2.2 → 0.2.4
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/README.md +4 -4
- package/dist/client.js +108 -48
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
DSH Web 입력창에서 모델과 추론 강도를 선택합니다. 하나의 선택 버튼을 누르면 추론 강도 팝오버가 먼저 열리며, 팝오버 안의 모델명을 눌러 모델 목록으로 이동합니다.
|
|
5
|
+
DSH Web 입력창에서 모델과 추론 강도를 선택합니다. 하나의 선택 버튼을 누르면 추론 강도 팝오버가 먼저 열리며, 팝오버 안의 모델명을 눌러 모델 목록으로 이동합니다. 모델을 고르면 실제 모델은 바로 바뀌지 않고 추론 강도 슬라이더가 이어서 표시되며, 강도를 설정할 때 모델이 함께 적용됩니다.
|
|
6
6
|
|
|
7
7
|
위 이미지는 플러그인 컴포넌트에 예시 모델 목록과 테마 색상을 제공한 검증 화면을 각각 캡처해 합성했습니다. 표시된 모델명과 추론 단계는 예시이며, 실제 목록은 연결한 제공자가 결정합니다.
|
|
8
8
|
|
|
@@ -10,14 +10,14 @@ DSH Web 입력창에서 모델과 추론 강도를 선택합니다. 하나의
|
|
|
10
10
|
|
|
11
11
|
1. DSH 플러그인 설치 화면에서 `@dungsil/dsh-plugin-ui-model-picker`를 설치하고 활성화합니다.
|
|
12
12
|
2. 입력창 오른쪽 선택 버튼을 눌러 추론 강도 슬라이더를 엽니다. 방향키와 Home/End 키도 사용할 수 있습니다.
|
|
13
|
-
3. 모델을 바꾸려면 팝오버 상단의 모델명 부분을 누르고 제공자별 모델을 선택합니다. 오른쪽 위 재설정 아이콘은 기본 추론 강도를 복원합니다.
|
|
13
|
+
3. 모델을 바꾸려면 팝오버 상단의 모델명 부분을 누르고 제공자별 모델을 선택합니다. 추론 강도를 지원하는 모델을 고르면 슬라이더가 이어서 나타나며, 강도를 설정해야 모델이 적용됩니다. 오른쪽 위 재설정 아이콘은 기본 추론 강도를 복원합니다.
|
|
14
14
|
|
|
15
15
|
## 동작과 호환성
|
|
16
16
|
|
|
17
17
|
- DSH `0.2.1-rc.5` 이상 `0.3.0` 미만의 Web UI를 지원합니다. 기본 `ui-model-selection` 기능이 활성화되어 있어야 합니다.
|
|
18
18
|
- 공용 모델 디렉터리와 선택 API를 그대로 사용하므로 `/model` 명령과 선택 상태를 공유합니다. 별도 모델 설정을 저장하거나 API 키를 다루지 않습니다.
|
|
19
|
-
- 실제 제공자 목록과 모델별 추론 메타데이터만 사용합니다. 별도 모델 목록, Fast 모드, 고정 추론 단계는 추가하지 않습니다. 추론을 지원하지 않는 모델에는 슬라이더를 표시하지 않으며,
|
|
20
|
-
- 다른 모델로 바꾸면
|
|
19
|
+
- 실제 제공자 목록과 모델별 추론 메타데이터만 사용합니다. 별도 모델 목록, Fast 모드, 고정 추론 단계는 추가하지 않습니다. 추론을 지원하지 않는 모델에는 슬라이더를 표시하지 않으며, 그런 모델은 목록에서 고르는 즉시 적용됩니다.
|
|
20
|
+
- 다른 모델로 바꾸면 그 모델이 지원하는 추론 강도에서 슬라이더가 시작되고, 강도를 설정해야 모델 선택이 적용됩니다. 슬라이더를 건드리지 않고 팝오버를 닫으면 모델은 바뀌지 않습니다. 기본값 재설정은 별도 강도 지정을 해제합니다.
|
|
21
21
|
- Escape, 바깥 클릭, 스크롤, 창 크기 변경으로 메뉴를 닫습니다. 메뉴의 방향키와 Tab 탐색을 지원합니다. 한국어·영어·중국어(간체)와 DSH 테마 색상을 따릅니다.
|
|
22
22
|
- 기본 모델 슬롯만 우선순위로 대체합니다. 제거하면 기본 선택 UI가 돌아오며 모델 선택은 유지됩니다. 같은 슬롯을 대체하는 다른 플러그인과는 함께 사용하지 마세요.
|
|
23
23
|
|
package/dist/client.js
CHANGED
|
@@ -36,7 +36,7 @@ const composerStyles = `
|
|
|
36
36
|
.dsh-plugin-composer-chevron, .dsh-plugin-composer-detail { color: var(--dsw-alias-label-caption); }
|
|
37
37
|
.dsh-plugin-composer-trigger[aria-expanded=true] .dsh-plugin-composer-chevron { transform: rotate(180deg); }
|
|
38
38
|
`;
|
|
39
|
-
const ComposerTrigger = (0, react.forwardRef)(function ComposerTrigger({ className = "", icon, label, detail, ...props }, ref) {
|
|
39
|
+
const ComposerTrigger = /* @__PURE__ */ (0, react.forwardRef)(function ComposerTrigger({ className = "", icon, label, detail, ...props }, ref) {
|
|
40
40
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
41
41
|
...props,
|
|
42
42
|
ref,
|
|
@@ -108,6 +108,7 @@ const styles = `${composerStyles}
|
|
|
108
108
|
.dsh-codex-slider input::-moz-range-track { height: 28px; background: transparent; }
|
|
109
109
|
.dsh-codex-slider input::-moz-range-thumb { width: 26px; height: 26px; border: 1px solid #0000000a; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px #0002; }
|
|
110
110
|
.dsh-codex-notice { padding: 6px 10px; margin: 0; font-size: 12px; overflow-wrap: anywhere; }
|
|
111
|
+
.dsh-codex-status { position: absolute; width: 1px; height: 1px; padding: 0; margin: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
|
|
111
112
|
.dsh-codex-popover [role=alert] { color: var(--dsw-alias-state-error-primary, #b54424); }
|
|
112
113
|
.dsh-codex-popover :focus-visible { outline: 2px solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); outline-offset: -2px; }
|
|
113
114
|
.dsh-codex-popover :disabled { color: var(--dsw-alias-label-dimmed); cursor: default; }
|
|
@@ -127,7 +128,6 @@ const copy = {
|
|
|
127
128
|
model: "모델 선택",
|
|
128
129
|
effort: "추론 강도",
|
|
129
130
|
choose: "추론 수준 선택",
|
|
130
|
-
default: "제공자 기본값",
|
|
131
131
|
reset: "기본값으로 재설정",
|
|
132
132
|
loading: "모델을 불러오는 중입니다…",
|
|
133
133
|
empty: "사용할 수 있는 모델이 없습니다.",
|
|
@@ -141,7 +141,6 @@ const copy = {
|
|
|
141
141
|
model: "Select model",
|
|
142
142
|
effort: "Reasoning effort",
|
|
143
143
|
choose: "Select reasoning effort",
|
|
144
|
-
default: "Provider default",
|
|
145
144
|
reset: "Reset to default",
|
|
146
145
|
loading: "Loading models…",
|
|
147
146
|
empty: "No models available.",
|
|
@@ -155,7 +154,6 @@ const copy = {
|
|
|
155
154
|
model: "选择模型",
|
|
156
155
|
effort: "推理强度",
|
|
157
156
|
choose: "选择推理级别",
|
|
158
|
-
default: "提供商默认值",
|
|
159
157
|
reset: "重置为默认值",
|
|
160
158
|
loading: "正在加载模型…",
|
|
161
159
|
empty: "没有可用的模型。",
|
|
@@ -173,25 +171,29 @@ function apply(ctx) {
|
|
|
173
171
|
const state = (0, react.useSyncExternalStore)(subscribe, getSnapshot);
|
|
174
172
|
const { active: locale } = useLocaleSnapshot(ctx.locale);
|
|
175
173
|
const text = copy[locale.startsWith("ko") ? "ko" : locale.startsWith("zh") ? "zh" : "en"];
|
|
176
|
-
const [
|
|
174
|
+
const [requestedPanel, setPanel] = (0, react.useState)(null);
|
|
175
|
+
const [staged, setStaged] = (0, react.useState)(null);
|
|
177
176
|
const [draft, setDraft] = (0, react.useState)(0);
|
|
178
177
|
const [failed, setFailed] = (0, react.useState)(false);
|
|
179
178
|
const trigger = (0, react.useRef)(null);
|
|
180
179
|
const anchor = (0, react.useRef)(null);
|
|
181
180
|
const popup = (0, react.useRef)(null);
|
|
182
181
|
const pending = (0, react.useRef)(false);
|
|
182
|
+
const sliderSelection = (0, react.useRef)(null);
|
|
183
183
|
const mounted = (0, react.useRef)(true);
|
|
184
184
|
const id = (0, react.useId)();
|
|
185
185
|
const current = state.current;
|
|
186
186
|
const model = state.groups.find((item) => item.id === current?.provider)?.models.find((item) => item.id === current?.model);
|
|
187
187
|
const reasoning = model?.reasoning;
|
|
188
|
-
const efforts = reasoning?.efforts
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
188
|
+
const efforts = reasoning?.efforts ?? [];
|
|
189
|
+
const stagedGroup = staged ? state.groups.find((item) => item.id === staged.provider) : void 0;
|
|
190
|
+
const stagedModel = staged ? stagedGroup?.models.find((item) => item.id === staged.model) : model;
|
|
191
|
+
const stagedEfforts = (stagedModel?.reasoning)?.efforts ?? [];
|
|
192
|
+
const panel = requestedPanel === "effort" && !stagedEfforts.length ? "model" : requestedPanel;
|
|
192
193
|
const effective = current?.reasoningEffort ?? reasoning?.defaultEffort;
|
|
193
194
|
const effortIndex = efforts.findIndex((item) => item.id === effective);
|
|
194
|
-
const effortLabel = efforts[effortIndex]?.name ?? state.retainedEffort;
|
|
195
|
+
const effortLabel = efforts.length ? efforts[effortIndex]?.name ?? state.retainedEffort : void 0;
|
|
196
|
+
const stagedName = stagedModel?.name ?? (staged ? staged.model : current?.model) ?? text.model;
|
|
195
197
|
const busy = state.status === "selecting";
|
|
196
198
|
const disabled = locked || busy;
|
|
197
199
|
(0, react.useEffect)(() => {
|
|
@@ -202,10 +204,16 @@ function apply(ctx) {
|
|
|
202
204
|
};
|
|
203
205
|
}, [directory]);
|
|
204
206
|
const close = () => {
|
|
207
|
+
sliderSelection.current = null;
|
|
205
208
|
setPanel(null);
|
|
209
|
+
setStaged(null);
|
|
206
210
|
if (trigger.current) (0, _deepseek_ai_dsh_client_ui_primitives.focusWithoutRing)(trigger.current);
|
|
207
211
|
};
|
|
208
|
-
(0, _deepseek_ai_dsh_client_ui_primitives.useDismissOnOutsidePointer)(anchor, panel !== null, () =>
|
|
212
|
+
(0, _deepseek_ai_dsh_client_ui_primitives.useDismissOnOutsidePointer)(anchor, panel !== null, () => {
|
|
213
|
+
sliderSelection.current = null;
|
|
214
|
+
setPanel(null);
|
|
215
|
+
setStaged(null);
|
|
216
|
+
}, popup);
|
|
209
217
|
(0, react.useLayoutEffect)(() => {
|
|
210
218
|
if (!panel || !popup.current || !anchor.current) return;
|
|
211
219
|
const element = popup.current;
|
|
@@ -251,25 +259,42 @@ function apply(ctx) {
|
|
|
251
259
|
};
|
|
252
260
|
}, [panel]);
|
|
253
261
|
(0, react.useEffect)(() => {
|
|
254
|
-
if (locked)
|
|
262
|
+
if (locked) {
|
|
263
|
+
sliderSelection.current = null;
|
|
264
|
+
setPanel(null);
|
|
265
|
+
setStaged(null);
|
|
266
|
+
}
|
|
255
267
|
}, [locked]);
|
|
256
268
|
(0, react.useEffect)(() => {
|
|
269
|
+
const selection = sliderSelection.current;
|
|
270
|
+
sliderSelection.current = null;
|
|
271
|
+
if (selection && current?.provider === selection.provider && current?.model === selection.model && current?.reasoningEffort === selection.reasoningEffort) {
|
|
272
|
+
setStaged(null);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
257
275
|
setPanel(null);
|
|
276
|
+
setStaged(null);
|
|
258
277
|
}, [
|
|
259
278
|
current?.provider,
|
|
260
279
|
current?.model,
|
|
261
280
|
current?.reasoningEffort
|
|
262
281
|
]);
|
|
263
|
-
const select = async (selection) => {
|
|
282
|
+
const select = async (selection, keepSlider = false) => {
|
|
264
283
|
if (disabled || pending.current) return;
|
|
265
284
|
pending.current = true;
|
|
285
|
+
sliderSelection.current = keepSlider ? selection : null;
|
|
266
286
|
setFailed(false);
|
|
267
287
|
try {
|
|
268
288
|
const result = await directory.select(selection);
|
|
269
289
|
if (!mounted.current) return;
|
|
270
|
-
if (result.ok)
|
|
271
|
-
|
|
290
|
+
if (result.ok) {
|
|
291
|
+
if (!keepSlider) close();
|
|
292
|
+
} else {
|
|
293
|
+
sliderSelection.current = null;
|
|
294
|
+
setFailed(true);
|
|
295
|
+
}
|
|
272
296
|
} catch {
|
|
297
|
+
sliderSelection.current = null;
|
|
273
298
|
if (mounted.current) setFailed(true);
|
|
274
299
|
} finally {
|
|
275
300
|
pending.current = false;
|
|
@@ -281,19 +306,50 @@ function apply(ctx) {
|
|
|
281
306
|
close();
|
|
282
307
|
return;
|
|
283
308
|
}
|
|
309
|
+
setStaged(null);
|
|
284
310
|
setDraft(Math.max(0, effortIndex));
|
|
285
311
|
setFailed(false);
|
|
286
312
|
setPanel("effort");
|
|
287
313
|
directory.load().catch(() => {});
|
|
288
314
|
};
|
|
289
315
|
const selectEffort = (index) => {
|
|
290
|
-
|
|
291
|
-
const effort = efforts[index].id;
|
|
292
|
-
select({
|
|
316
|
+
const target = staged ?? (current ? {
|
|
293
317
|
provider: current.provider,
|
|
294
|
-
model: current.model
|
|
295
|
-
|
|
318
|
+
model: current.model
|
|
319
|
+
} : null);
|
|
320
|
+
const effort = stagedEfforts[index];
|
|
321
|
+
if (!target || !effort) return;
|
|
322
|
+
select({
|
|
323
|
+
provider: target.provider,
|
|
324
|
+
model: target.model,
|
|
325
|
+
reasoningEffort: effort.id
|
|
326
|
+
}, true);
|
|
327
|
+
};
|
|
328
|
+
const stageModel = (provider, option) => {
|
|
329
|
+
const same = provider === current?.provider && option.id === current?.model;
|
|
330
|
+
const optionReasoning = option.reasoning;
|
|
331
|
+
const optionEfforts = optionReasoning?.efforts ?? [];
|
|
332
|
+
if (optionEfforts.length < 2) {
|
|
333
|
+
if (same) {
|
|
334
|
+
close();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const effort = optionReasoning?.defaultEffort;
|
|
338
|
+
select({
|
|
339
|
+
provider,
|
|
340
|
+
model: option.id,
|
|
341
|
+
...effort === void 0 ? {} : { reasoningEffort: effort }
|
|
342
|
+
});
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const preferredEffort = same ? current?.reasoningEffort ?? optionReasoning?.defaultEffort : optionReasoning?.defaultEffort ?? current?.reasoningEffort;
|
|
346
|
+
setStaged({
|
|
347
|
+
provider,
|
|
348
|
+
model: option.id
|
|
296
349
|
});
|
|
350
|
+
setDraft(Math.max(0, optionEfforts.findIndex((item) => item.id === preferredEffort)));
|
|
351
|
+
setFailed(false);
|
|
352
|
+
setPanel("effort");
|
|
297
353
|
};
|
|
298
354
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
299
355
|
className: "dsh-codex-picker",
|
|
@@ -304,13 +360,16 @@ function apply(ctx) {
|
|
|
304
360
|
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDataOutlineRegular, { size: 16 }),
|
|
305
361
|
label: model?.name ?? current?.model ?? text.model,
|
|
306
362
|
detail: effortLabel,
|
|
307
|
-
disabled,
|
|
308
|
-
"aria-
|
|
363
|
+
disabled: locked,
|
|
364
|
+
"aria-disabled": disabled,
|
|
365
|
+
"aria-label": `${efforts.length ? text.choose : text.model}: ${model?.name ?? current?.model ?? text.model}${effortLabel ? `, ${effortLabel}` : ""}`,
|
|
309
366
|
title: `${model?.name ?? current?.model ?? text.model}${effortLabel ? ` ${effortLabel}` : ""}`,
|
|
310
367
|
"aria-haspopup": "dialog",
|
|
311
368
|
"aria-expanded": panel !== null,
|
|
312
369
|
"aria-controls": panel ? id : void 0,
|
|
313
|
-
onClick: (event) =>
|
|
370
|
+
onClick: (event) => {
|
|
371
|
+
if (!disabled) show(event.currentTarget);
|
|
372
|
+
}
|
|
314
373
|
})
|
|
315
374
|
}), panel && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.MenuSurface, {
|
|
316
375
|
id,
|
|
@@ -319,7 +378,7 @@ function apply(ctx) {
|
|
|
319
378
|
"aria-label": panel === "model" ? text.model : text.effort,
|
|
320
379
|
className: "dsh-codex-popover",
|
|
321
380
|
children: [
|
|
322
|
-
panel === "model" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
381
|
+
panel === "model" ? stagedEfforts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
323
382
|
type: "button",
|
|
324
383
|
className: "dsh-codex-back",
|
|
325
384
|
onClick: () => setPanel("effort"),
|
|
@@ -333,25 +392,30 @@ function apply(ctx) {
|
|
|
333
392
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
334
393
|
type: "button",
|
|
335
394
|
className: "dsh-codex-model",
|
|
336
|
-
disabled,
|
|
337
|
-
"aria-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
395
|
+
disabled: locked,
|
|
396
|
+
"aria-disabled": disabled,
|
|
397
|
+
"aria-label": `${text.model}: ${stagedName}`,
|
|
398
|
+
onClick: () => {
|
|
399
|
+
if (!disabled) setPanel("model");
|
|
400
|
+
},
|
|
401
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: stagedEfforts[draft]?.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
402
|
+
stagedName,
|
|
341
403
|
" ",
|
|
342
404
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutlineRegular, { size: 14 })
|
|
343
405
|
] })]
|
|
344
406
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
345
407
|
type: "button",
|
|
346
408
|
className: "dsh-codex-reset",
|
|
347
|
-
disabled:
|
|
409
|
+
disabled: locked || !(staged ?? current),
|
|
410
|
+
"aria-disabled": disabled || !(staged ?? current),
|
|
348
411
|
"aria-label": text.reset,
|
|
349
412
|
title: text.reset,
|
|
350
413
|
onClick: () => {
|
|
351
|
-
|
|
414
|
+
const target = staged ?? (current ? {
|
|
352
415
|
provider: current.provider,
|
|
353
416
|
model: current.model
|
|
354
|
-
});
|
|
417
|
+
} : null);
|
|
418
|
+
if (target) select(target);
|
|
355
419
|
},
|
|
356
420
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconResetOutlineRegular, {})
|
|
357
421
|
})]
|
|
@@ -362,7 +426,7 @@ function apply(ctx) {
|
|
|
362
426
|
children: text.loading
|
|
363
427
|
}),
|
|
364
428
|
busy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
365
|
-
className: "dsh-codex-notice",
|
|
429
|
+
className: panel === "effort" ? "dsh-codex-status" : "dsh-codex-notice",
|
|
366
430
|
role: "status",
|
|
367
431
|
children: text.busy
|
|
368
432
|
}),
|
|
@@ -396,14 +460,7 @@ function apply(ctx) {
|
|
|
396
460
|
"aria-checked": provider.id === current?.provider && option.id === current?.model,
|
|
397
461
|
className: "dsh-codex-option",
|
|
398
462
|
disabled,
|
|
399
|
-
onClick: () =>
|
|
400
|
-
const effort = provider.id === current?.provider && option.id === current?.model ? current.reasoningEffort : option.reasoning?.defaultEffort;
|
|
401
|
-
select({
|
|
402
|
-
provider: provider.id,
|
|
403
|
-
model: option.id,
|
|
404
|
-
...effort === void 0 ? {} : { reasoningEffort: effort }
|
|
405
|
-
});
|
|
406
|
-
},
|
|
463
|
+
onClick: () => stageModel(provider.id, option),
|
|
407
464
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [option.name, option.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: option.description })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
408
465
|
className: "dsh-codex-check",
|
|
409
466
|
"aria-hidden": "true",
|
|
@@ -427,26 +484,29 @@ function apply(ctx) {
|
|
|
427
484
|
},
|
|
428
485
|
children: text.retry
|
|
429
486
|
})
|
|
430
|
-
] }) :
|
|
487
|
+
] }) : stagedEfforts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
431
488
|
className: "dsh-codex-slider",
|
|
432
489
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
433
490
|
className: "dsh-codex-rail",
|
|
434
|
-
style: { "--dsh-codex-progress": `${
|
|
491
|
+
style: { "--dsh-codex-progress": `${stagedEfforts.length > 1 ? draft / (stagedEfforts.length - 1) * 100 : 0}%` },
|
|
435
492
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
436
493
|
className: "dsh-codex-track",
|
|
437
494
|
"aria-hidden": "true",
|
|
438
|
-
children:
|
|
495
|
+
children: stagedEfforts.map((effort, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { "data-filled": index <= draft }, effort.id))
|
|
439
496
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
440
497
|
id: `${id}-effort`,
|
|
441
498
|
type: "range",
|
|
442
499
|
min: 0,
|
|
443
|
-
max:
|
|
500
|
+
max: stagedEfforts.length - 1,
|
|
444
501
|
step: 1,
|
|
445
502
|
value: draft,
|
|
446
|
-
disabled:
|
|
503
|
+
disabled: locked || stagedEfforts.length < 2,
|
|
504
|
+
"aria-disabled": disabled || stagedEfforts.length < 2,
|
|
447
505
|
"aria-label": text.effort,
|
|
448
|
-
"aria-valuetext":
|
|
449
|
-
onChange: (event) =>
|
|
506
|
+
"aria-valuetext": stagedEfforts[draft]?.name,
|
|
507
|
+
onChange: (event) => {
|
|
508
|
+
if (!disabled) setDraft(Number(event.target.value));
|
|
509
|
+
},
|
|
450
510
|
onPointerUp: (event) => selectEffort(Number(event.currentTarget.value)),
|
|
451
511
|
onKeyUp: (event) => {
|
|
452
512
|
if ([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dungsil/dsh-plugin-ui-model-picker",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Model and reasoning effort picker for the DeepSeek Harness composer.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"@dungsil/dsh-ui": "0.0.0",
|
|
41
41
|
"@types/react": "18.3.31",
|
|
42
42
|
"@types/react-dom": "18.3.7",
|
|
43
|
+
"happy-dom": "21.0.4",
|
|
43
44
|
"react": "18.3.1",
|
|
44
45
|
"react-dom": "18.3.1",
|
|
45
46
|
"tsdown": "0.23.4"
|
|
@@ -88,6 +89,7 @@
|
|
|
88
89
|
},
|
|
89
90
|
"scripts": {
|
|
90
91
|
"build": "tsdown",
|
|
91
|
-
"typecheck": "tsc --noEmit"
|
|
92
|
+
"typecheck": "tsc --noEmit",
|
|
93
|
+
"test": "node --test test/*.test.mjs"
|
|
92
94
|
}
|
|
93
95
|
}
|