dsh-sub2api-sync 0.2.4 → 0.2.5
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.cjs +287 -40
- package/lib/client.js +287 -40
- package/package.json +1 -1
package/lib/client.cjs
CHANGED
|
@@ -132,6 +132,8 @@ const zh = {
|
|
|
132
132
|
keysTitle: "Keys",
|
|
133
133
|
createKey: "创建 key",
|
|
134
134
|
keyGroupLabel: "所属分组",
|
|
135
|
+
keyGroupSearch: "搜索分组…",
|
|
136
|
+
keyGroupSearchEmpty: "没有匹配的分组",
|
|
135
137
|
keyNameLabel: "Key 名称",
|
|
136
138
|
keyNameHint: "默认与分组同名",
|
|
137
139
|
keyGroupPlaceholder: "选择分组",
|
|
@@ -246,6 +248,8 @@ const en = {
|
|
|
246
248
|
keysTitle: "Keys",
|
|
247
249
|
createKey: "Create key",
|
|
248
250
|
keyGroupLabel: "Group",
|
|
251
|
+
keyGroupSearch: "Search groups…",
|
|
252
|
+
keyGroupSearchEmpty: "No groups match",
|
|
249
253
|
keyNameLabel: "Key name",
|
|
250
254
|
keyNameHint: "Defaults to the group name",
|
|
251
255
|
keyGroupPlaceholder: "Pick a group",
|
|
@@ -725,30 +729,135 @@ const CSS = `
|
|
|
725
729
|
flex: 1 1 220px;
|
|
726
730
|
}
|
|
727
731
|
|
|
728
|
-
/*
|
|
729
|
-
footprint as the sibling Input rows
|
|
730
|
-
.
|
|
732
|
+
/* Group picker (create-key dialog): the trigger takes the same field-column
|
|
733
|
+
footprint as the sibling Input rows, painted like an input rather than a
|
|
734
|
+
button so both rows read as one form. */
|
|
735
|
+
.s2s-groupPicker {
|
|
731
736
|
display: flex;
|
|
732
737
|
flex: 1 1 220px;
|
|
733
738
|
min-width: 0;
|
|
734
739
|
}
|
|
735
740
|
|
|
736
|
-
.s2s-
|
|
737
|
-
width: 100%;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
/* Select-style trigger (group picker): label left, chevron right. */
|
|
741
|
-
.s2s-selectTrigger {
|
|
741
|
+
.s2s-selectField {
|
|
742
742
|
display: flex;
|
|
743
743
|
align-items: center;
|
|
744
744
|
justify-content: space-between;
|
|
745
745
|
gap: 8px;
|
|
746
746
|
width: 100%;
|
|
747
|
+
min-height: 34px;
|
|
748
|
+
padding: 6px 10px;
|
|
749
|
+
box-sizing: border-box;
|
|
750
|
+
border: 0.5px solid var(--dsw-alias-border-l2);
|
|
751
|
+
border-radius: var(--dsw-radius-md);
|
|
752
|
+
background: transparent;
|
|
753
|
+
color: inherit;
|
|
754
|
+
font: inherit;
|
|
755
|
+
text-align: left;
|
|
756
|
+
cursor: pointer;
|
|
757
|
+
user-select: none;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
.s2s-selectField:disabled {
|
|
761
|
+
opacity: 0.55;
|
|
762
|
+
cursor: default;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.s2s-selectField:focus-visible {
|
|
766
|
+
outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
767
|
+
outline-offset: 2px;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
.s2s-selectPlaceholder {
|
|
771
|
+
color: var(--dsw-alias-label-tertiary);
|
|
772
|
+
overflow: hidden;
|
|
773
|
+
text-overflow: ellipsis;
|
|
774
|
+
white-space: nowrap;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.s2s-selectValue {
|
|
778
|
+
min-width: 0;
|
|
779
|
+
overflow: hidden;
|
|
780
|
+
text-overflow: ellipsis;
|
|
781
|
+
white-space: nowrap;
|
|
747
782
|
}
|
|
748
783
|
|
|
749
784
|
.s2s-selectChevron {
|
|
750
785
|
flex: none;
|
|
751
786
|
opacity: 0.55;
|
|
787
|
+
transition: transform var(--ds-transition-duration) var(--ds-ease-in-out);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
.s2s-selectChevronOpen {
|
|
791
|
+
transform: rotate(180deg);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/* The options panel: fixed-positioned from the trigger (escapes the modal
|
|
795
|
+
card's overflow clip), width-matched to it, search pinned on top, options
|
|
796
|
+
scrolling below. */
|
|
797
|
+
.s2s-groupPanel {
|
|
798
|
+
display: flex;
|
|
799
|
+
flex-direction: column;
|
|
800
|
+
box-sizing: border-box;
|
|
801
|
+
z-index: 10;
|
|
802
|
+
overflow: hidden;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
.s2s-groupSearch {
|
|
806
|
+
flex: none;
|
|
807
|
+
padding: 8px 8px 6px;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
.s2s-groupSearch > * {
|
|
811
|
+
width: 100%;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
.s2s-groupList {
|
|
815
|
+
flex: 1 1 auto;
|
|
816
|
+
min-height: 0;
|
|
817
|
+
overflow-y: auto;
|
|
818
|
+
overscroll-behavior: contain;
|
|
819
|
+
padding: 0 8px 8px;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.s2s-groupEmpty {
|
|
823
|
+
margin: 0;
|
|
824
|
+
padding: 10px 4px;
|
|
825
|
+
color: var(--dsw-alias-label-tertiary);
|
|
826
|
+
text-align: center;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.s2s-groupOption {
|
|
830
|
+
display: flex;
|
|
831
|
+
align-items: center;
|
|
832
|
+
gap: 8px;
|
|
833
|
+
width: 100%;
|
|
834
|
+
min-height: 32px;
|
|
835
|
+
padding: 6px 10px;
|
|
836
|
+
box-sizing: border-box;
|
|
837
|
+
border: 0;
|
|
838
|
+
border-radius: var(--dsw-radius-md);
|
|
839
|
+
background: transparent;
|
|
840
|
+
color: inherit;
|
|
841
|
+
font: inherit;
|
|
842
|
+
text-align: left;
|
|
843
|
+
cursor: pointer;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
.s2s-groupOption[data-active='true'] {
|
|
847
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
.s2s-groupOptionName {
|
|
851
|
+
flex: 1 1 auto;
|
|
852
|
+
min-width: 0;
|
|
853
|
+
overflow: hidden;
|
|
854
|
+
text-overflow: ellipsis;
|
|
855
|
+
white-space: nowrap;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
.s2s-groupOptionCheck {
|
|
859
|
+
flex: none;
|
|
860
|
+
color: var(--dsw-alias-state-business-primary);
|
|
752
861
|
}
|
|
753
862
|
|
|
754
863
|
/* The keys-card search input: fixed-width slot between the title and the
|
|
@@ -1178,6 +1287,164 @@ function protocolForPlatform(platform) {
|
|
|
1178
1287
|
}
|
|
1179
1288
|
}
|
|
1180
1289
|
|
|
1290
|
+
//#endregion
|
|
1291
|
+
//#region src/client/section/GroupPicker.tsx
|
|
1292
|
+
/** Design cap for the panel; the viewport clamp never exceeds it. */
|
|
1293
|
+
const PANEL_MAX_HEIGHT = 280;
|
|
1294
|
+
/** Distance between the trigger's bottom edge and the panel. */
|
|
1295
|
+
const PANEL_GAP = 6;
|
|
1296
|
+
function GroupPicker({ groups, value, placeholder, searchPlaceholder, emptyLabel, disabled = false, onSelect }) {
|
|
1297
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
1298
|
+
const [filter, setFilter] = (0, react.useState)("");
|
|
1299
|
+
const [activeIndex, setActiveIndex] = (0, react.useState)(0);
|
|
1300
|
+
const [triggerWidth, setTriggerWidth] = (0, react.useState)(0);
|
|
1301
|
+
const rootRef = (0, react.useRef)(null);
|
|
1302
|
+
const triggerRef = (0, react.useRef)(null);
|
|
1303
|
+
const panelRef = (0, react.useRef)(null);
|
|
1304
|
+
const listRef = (0, react.useRef)(null);
|
|
1305
|
+
const selected = value === "" ? undefined : groups.find((group) => group.id === value);
|
|
1306
|
+
const filtered = (0, react.useMemo)(() => {
|
|
1307
|
+
const needle = filter.trim().toLowerCase();
|
|
1308
|
+
if (needle === "") return groups;
|
|
1309
|
+
return groups.filter((group) => group.name.toLowerCase().includes(needle));
|
|
1310
|
+
}, [groups, filter]);
|
|
1311
|
+
const position = (0, __deepseek_ai_dsh_client_ui_primitives.useAnchoredPosition)({
|
|
1312
|
+
open,
|
|
1313
|
+
anchorRef: triggerRef,
|
|
1314
|
+
panelRef,
|
|
1315
|
+
side: "bottom",
|
|
1316
|
+
align: "start",
|
|
1317
|
+
gap: PANEL_GAP,
|
|
1318
|
+
margin: 24
|
|
1319
|
+
});
|
|
1320
|
+
const maxHeight = (0, __deepseek_ai_dsh_client_ui_primitives.useAnchoredMaxHeight)(panelRef, PANEL_MAX_HEIGHT, open ? filtered.length : 0);
|
|
1321
|
+
(0, __deepseek_ai_dsh_client_ui_primitives.useDismissOnOutsidePointer)(rootRef, open, setOpen, panelRef);
|
|
1322
|
+
(0, react.useEffect)(() => {
|
|
1323
|
+
if (!open) return;
|
|
1324
|
+
setFilter("");
|
|
1325
|
+
setActiveIndex(0);
|
|
1326
|
+
setTriggerWidth(triggerRef.current?.getBoundingClientRect().width ?? 0);
|
|
1327
|
+
const timer$1 = window.setTimeout(() => {
|
|
1328
|
+
panelRef.current?.querySelector("input")?.focus();
|
|
1329
|
+
}, 0);
|
|
1330
|
+
return () => window.clearTimeout(timer$1);
|
|
1331
|
+
}, [open]);
|
|
1332
|
+
(0, react.useEffect)(() => {
|
|
1333
|
+
if (!open) return;
|
|
1334
|
+
const onResize = () => {
|
|
1335
|
+
setTriggerWidth(triggerRef.current?.getBoundingClientRect().width ?? 0);
|
|
1336
|
+
};
|
|
1337
|
+
window.addEventListener("resize", onResize);
|
|
1338
|
+
return () => window.removeEventListener("resize", onResize);
|
|
1339
|
+
}, [open]);
|
|
1340
|
+
(0, react.useEffect)(() => {
|
|
1341
|
+
const list = listRef.current;
|
|
1342
|
+
if (list === null) return;
|
|
1343
|
+
list.querySelector("[data-active='true']")?.scrollIntoView({ block: "nearest" });
|
|
1344
|
+
}, [activeIndex, filter]);
|
|
1345
|
+
const close = () => {
|
|
1346
|
+
setOpen(false);
|
|
1347
|
+
triggerRef.current?.focus();
|
|
1348
|
+
};
|
|
1349
|
+
const pick = (group) => {
|
|
1350
|
+
onSelect(group);
|
|
1351
|
+
close();
|
|
1352
|
+
};
|
|
1353
|
+
const onKeyDown = (event) => {
|
|
1354
|
+
if (event.key === "Escape") {
|
|
1355
|
+
event.stopPropagation();
|
|
1356
|
+
close();
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
if (event.key === "ArrowDown") {
|
|
1360
|
+
event.preventDefault();
|
|
1361
|
+
if (filtered.length > 0) setActiveIndex((index) => Math.min(index + 1, filtered.length - 1));
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
if (event.key === "ArrowUp") {
|
|
1365
|
+
event.preventDefault();
|
|
1366
|
+
setActiveIndex((index) => Math.max(index - 1, 0));
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
if (event.key === "Enter") {
|
|
1370
|
+
event.preventDefault();
|
|
1371
|
+
const group = filtered[activeIndex];
|
|
1372
|
+
if (group !== undefined) pick(group);
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1376
|
+
className: "s2s-groupPicker",
|
|
1377
|
+
ref: rootRef,
|
|
1378
|
+
children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
1379
|
+
type: "button",
|
|
1380
|
+
ref: triggerRef,
|
|
1381
|
+
className: "s2s-selectField",
|
|
1382
|
+
disabled,
|
|
1383
|
+
"aria-haspopup": "listbox",
|
|
1384
|
+
"aria-expanded": open,
|
|
1385
|
+
onClick: () => {
|
|
1386
|
+
setOpen((next) => !next);
|
|
1387
|
+
},
|
|
1388
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1389
|
+
className: selected === undefined ? "s2s-selectPlaceholder" : "s2s-selectValue",
|
|
1390
|
+
children: selected === undefined ? placeholder : selected.name
|
|
1391
|
+
}), (0, react_jsx_runtime.jsx)(__deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
|
|
1392
|
+
size: 14,
|
|
1393
|
+
className: `s2s-selectChevron${open ? " s2s-selectChevronOpen" : ""}`
|
|
1394
|
+
})]
|
|
1395
|
+
}), open && (0, react_jsx_runtime.jsxs)(__deepseek_ai_dsh_client_ui_primitives.MenuSurface, {
|
|
1396
|
+
ref: panelRef,
|
|
1397
|
+
className: "s2s-groupPanel",
|
|
1398
|
+
role: "listbox",
|
|
1399
|
+
style: position === null ? { visibility: "hidden" } : {
|
|
1400
|
+
...position,
|
|
1401
|
+
...triggerWidth > 0 ? { width: `${triggerWidth}px` } : {},
|
|
1402
|
+
maxHeight: `${maxHeight}px`
|
|
1403
|
+
},
|
|
1404
|
+
onKeyDown,
|
|
1405
|
+
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
1406
|
+
className: "s2s-groupSearch",
|
|
1407
|
+
children: (0, react_jsx_runtime.jsx)(__deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
1408
|
+
type: "text",
|
|
1409
|
+
value: filter,
|
|
1410
|
+
placeholder: searchPlaceholder,
|
|
1411
|
+
onChange: (event) => {
|
|
1412
|
+
setFilter(event.target.value);
|
|
1413
|
+
setActiveIndex(0);
|
|
1414
|
+
}
|
|
1415
|
+
})
|
|
1416
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
1417
|
+
className: "s2s-groupList",
|
|
1418
|
+
ref: listRef,
|
|
1419
|
+
children: [filtered.length === 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
1420
|
+
className: "s2s-groupEmpty",
|
|
1421
|
+
children: emptyLabel
|
|
1422
|
+
}), filtered.map((group, index) => (0, react_jsx_runtime.jsxs)("button", {
|
|
1423
|
+
type: "button",
|
|
1424
|
+
role: "option",
|
|
1425
|
+
"aria-selected": group.id === value,
|
|
1426
|
+
"data-active": index === activeIndex || undefined,
|
|
1427
|
+
className: "s2s-groupOption",
|
|
1428
|
+
onMouseEnter: () => {
|
|
1429
|
+
setActiveIndex(index);
|
|
1430
|
+
},
|
|
1431
|
+
onClick: () => {
|
|
1432
|
+
pick(group);
|
|
1433
|
+
},
|
|
1434
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1435
|
+
className: "s2s-groupOptionName",
|
|
1436
|
+
children: group.name
|
|
1437
|
+
}), group.id === value && (0, react_jsx_runtime.jsx)("span", {
|
|
1438
|
+
className: "s2s-groupOptionCheck",
|
|
1439
|
+
"aria-hidden": "true",
|
|
1440
|
+
children: "✓"
|
|
1441
|
+
})]
|
|
1442
|
+
}, group.id))]
|
|
1443
|
+
})]
|
|
1444
|
+
})]
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1181
1448
|
//#endregion
|
|
1182
1449
|
//#region src/client/section/KeysCard.tsx
|
|
1183
1450
|
const PAGE_SIZE = 5;
|
|
@@ -1194,7 +1461,6 @@ function KeysCard({ api, config, groups, t }) {
|
|
|
1194
1461
|
const [loading, setLoading] = (0, react.useState)(false);
|
|
1195
1462
|
const [listError, setListError] = (0, react.useState)(undefined);
|
|
1196
1463
|
const [creating, setCreating] = (0, react.useState)(false);
|
|
1197
|
-
const [groupMenuOpen, setGroupMenuOpen] = (0, react.useState)(false);
|
|
1198
1464
|
const [groupId, setGroupId] = (0, react.useState)("");
|
|
1199
1465
|
const [name$1, setName] = (0, react.useState)("");
|
|
1200
1466
|
const [nameTouched, setNameTouched] = (0, react.useState)(false);
|
|
@@ -1273,6 +1539,9 @@ else nextSet.delete(keyId);
|
|
|
1273
1539
|
setGroupId(gid);
|
|
1274
1540
|
if (!nameTouched) setName(groupNameOf(gid));
|
|
1275
1541
|
};
|
|
1542
|
+
const selectGroup = (group) => {
|
|
1543
|
+
pickGroup(group.id);
|
|
1544
|
+
};
|
|
1276
1545
|
const closeCreate = () => {
|
|
1277
1546
|
setCreating(false);
|
|
1278
1547
|
setGroupId("");
|
|
@@ -1461,36 +1730,14 @@ else nextSet.delete(keyId);
|
|
|
1461
1730
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1462
1731
|
className: "s2s-fieldLabel",
|
|
1463
1732
|
children: t("keyGroupLabel")
|
|
1464
|
-
}), (0, react_jsx_runtime.jsx)(
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
},
|
|
1473
|
-
children: (0, react_jsx_runtime.jsxs)("span", {
|
|
1474
|
-
className: "s2s-selectTrigger",
|
|
1475
|
-
children: [(0, react_jsx_runtime.jsx)("span", { children: groupId === "" ? t("keyGroupPlaceholder") : groupNameOf(groupId) }), (0, react_jsx_runtime.jsx)(__deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
|
|
1476
|
-
size: 14,
|
|
1477
|
-
className: "s2s-selectChevron"
|
|
1478
|
-
})]
|
|
1479
|
-
})
|
|
1480
|
-
})
|
|
1481
|
-
}),
|
|
1482
|
-
items: groups.map((group) => ({
|
|
1483
|
-
id: String(group.id),
|
|
1484
|
-
label: group.name
|
|
1485
|
-
})),
|
|
1486
|
-
selectedId: groupId === "" ? undefined : String(groupId),
|
|
1487
|
-
onSelect: (id) => {
|
|
1488
|
-
pickGroup(Number(id));
|
|
1489
|
-
setGroupMenuOpen(false);
|
|
1490
|
-
},
|
|
1491
|
-
onClose: () => {
|
|
1492
|
-
setGroupMenuOpen(false);
|
|
1493
|
-
}
|
|
1733
|
+
}), (0, react_jsx_runtime.jsx)(GroupPicker, {
|
|
1734
|
+
groups,
|
|
1735
|
+
value: groupId,
|
|
1736
|
+
placeholder: t("keyGroupPlaceholder"),
|
|
1737
|
+
searchPlaceholder: t("keyGroupSearch"),
|
|
1738
|
+
emptyLabel: t("keyGroupSearchEmpty"),
|
|
1739
|
+
disabled: busy,
|
|
1740
|
+
onSelect: selectGroup
|
|
1494
1741
|
})]
|
|
1495
1742
|
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
1496
1743
|
className: "s2s-inline",
|
package/lib/client.js
CHANGED
|
@@ -138,6 +138,8 @@ const zh = {
|
|
|
138
138
|
keysTitle: "Keys",
|
|
139
139
|
createKey: "创建 key",
|
|
140
140
|
keyGroupLabel: "所属分组",
|
|
141
|
+
keyGroupSearch: "搜索分组…",
|
|
142
|
+
keyGroupSearchEmpty: "没有匹配的分组",
|
|
141
143
|
keyNameLabel: "Key 名称",
|
|
142
144
|
keyNameHint: "默认与分组同名",
|
|
143
145
|
keyGroupPlaceholder: "选择分组",
|
|
@@ -252,6 +254,8 @@ const en = {
|
|
|
252
254
|
keysTitle: "Keys",
|
|
253
255
|
createKey: "Create key",
|
|
254
256
|
keyGroupLabel: "Group",
|
|
257
|
+
keyGroupSearch: "Search groups…",
|
|
258
|
+
keyGroupSearchEmpty: "No groups match",
|
|
255
259
|
keyNameLabel: "Key name",
|
|
256
260
|
keyNameHint: "Defaults to the group name",
|
|
257
261
|
keyGroupPlaceholder: "Pick a group",
|
|
@@ -731,30 +735,135 @@ const CSS = `
|
|
|
731
735
|
flex: 1 1 220px;
|
|
732
736
|
}
|
|
733
737
|
|
|
734
|
-
/*
|
|
735
|
-
footprint as the sibling Input rows
|
|
736
|
-
.
|
|
738
|
+
/* Group picker (create-key dialog): the trigger takes the same field-column
|
|
739
|
+
footprint as the sibling Input rows, painted like an input rather than a
|
|
740
|
+
button so both rows read as one form. */
|
|
741
|
+
.s2s-groupPicker {
|
|
737
742
|
display: flex;
|
|
738
743
|
flex: 1 1 220px;
|
|
739
744
|
min-width: 0;
|
|
740
745
|
}
|
|
741
746
|
|
|
742
|
-
.s2s-
|
|
743
|
-
width: 100%;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
/* Select-style trigger (group picker): label left, chevron right. */
|
|
747
|
-
.s2s-selectTrigger {
|
|
747
|
+
.s2s-selectField {
|
|
748
748
|
display: flex;
|
|
749
749
|
align-items: center;
|
|
750
750
|
justify-content: space-between;
|
|
751
751
|
gap: 8px;
|
|
752
752
|
width: 100%;
|
|
753
|
+
min-height: 34px;
|
|
754
|
+
padding: 6px 10px;
|
|
755
|
+
box-sizing: border-box;
|
|
756
|
+
border: 0.5px solid var(--dsw-alias-border-l2);
|
|
757
|
+
border-radius: var(--dsw-radius-md);
|
|
758
|
+
background: transparent;
|
|
759
|
+
color: inherit;
|
|
760
|
+
font: inherit;
|
|
761
|
+
text-align: left;
|
|
762
|
+
cursor: pointer;
|
|
763
|
+
user-select: none;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
.s2s-selectField:disabled {
|
|
767
|
+
opacity: 0.55;
|
|
768
|
+
cursor: default;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.s2s-selectField:focus-visible {
|
|
772
|
+
outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
773
|
+
outline-offset: 2px;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.s2s-selectPlaceholder {
|
|
777
|
+
color: var(--dsw-alias-label-tertiary);
|
|
778
|
+
overflow: hidden;
|
|
779
|
+
text-overflow: ellipsis;
|
|
780
|
+
white-space: nowrap;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.s2s-selectValue {
|
|
784
|
+
min-width: 0;
|
|
785
|
+
overflow: hidden;
|
|
786
|
+
text-overflow: ellipsis;
|
|
787
|
+
white-space: nowrap;
|
|
753
788
|
}
|
|
754
789
|
|
|
755
790
|
.s2s-selectChevron {
|
|
756
791
|
flex: none;
|
|
757
792
|
opacity: 0.55;
|
|
793
|
+
transition: transform var(--ds-transition-duration) var(--ds-ease-in-out);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
.s2s-selectChevronOpen {
|
|
797
|
+
transform: rotate(180deg);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/* The options panel: fixed-positioned from the trigger (escapes the modal
|
|
801
|
+
card's overflow clip), width-matched to it, search pinned on top, options
|
|
802
|
+
scrolling below. */
|
|
803
|
+
.s2s-groupPanel {
|
|
804
|
+
display: flex;
|
|
805
|
+
flex-direction: column;
|
|
806
|
+
box-sizing: border-box;
|
|
807
|
+
z-index: 10;
|
|
808
|
+
overflow: hidden;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
.s2s-groupSearch {
|
|
812
|
+
flex: none;
|
|
813
|
+
padding: 8px 8px 6px;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
.s2s-groupSearch > * {
|
|
817
|
+
width: 100%;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
.s2s-groupList {
|
|
821
|
+
flex: 1 1 auto;
|
|
822
|
+
min-height: 0;
|
|
823
|
+
overflow-y: auto;
|
|
824
|
+
overscroll-behavior: contain;
|
|
825
|
+
padding: 0 8px 8px;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
.s2s-groupEmpty {
|
|
829
|
+
margin: 0;
|
|
830
|
+
padding: 10px 4px;
|
|
831
|
+
color: var(--dsw-alias-label-tertiary);
|
|
832
|
+
text-align: center;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
.s2s-groupOption {
|
|
836
|
+
display: flex;
|
|
837
|
+
align-items: center;
|
|
838
|
+
gap: 8px;
|
|
839
|
+
width: 100%;
|
|
840
|
+
min-height: 32px;
|
|
841
|
+
padding: 6px 10px;
|
|
842
|
+
box-sizing: border-box;
|
|
843
|
+
border: 0;
|
|
844
|
+
border-radius: var(--dsw-radius-md);
|
|
845
|
+
background: transparent;
|
|
846
|
+
color: inherit;
|
|
847
|
+
font: inherit;
|
|
848
|
+
text-align: left;
|
|
849
|
+
cursor: pointer;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
.s2s-groupOption[data-active='true'] {
|
|
853
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
.s2s-groupOptionName {
|
|
857
|
+
flex: 1 1 auto;
|
|
858
|
+
min-width: 0;
|
|
859
|
+
overflow: hidden;
|
|
860
|
+
text-overflow: ellipsis;
|
|
861
|
+
white-space: nowrap;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
.s2s-groupOptionCheck {
|
|
865
|
+
flex: none;
|
|
866
|
+
color: var(--dsw-alias-state-business-primary);
|
|
758
867
|
}
|
|
759
868
|
|
|
760
869
|
/* The keys-card search input: fixed-width slot between the title and the
|
|
@@ -1184,6 +1293,164 @@ function protocolForPlatform(platform) {
|
|
|
1184
1293
|
}
|
|
1185
1294
|
}
|
|
1186
1295
|
|
|
1296
|
+
//#endregion
|
|
1297
|
+
//#region src/client/section/GroupPicker.tsx
|
|
1298
|
+
/** Design cap for the panel; the viewport clamp never exceeds it. */
|
|
1299
|
+
const PANEL_MAX_HEIGHT = 280;
|
|
1300
|
+
/** Distance between the trigger's bottom edge and the panel. */
|
|
1301
|
+
const PANEL_GAP = 6;
|
|
1302
|
+
function GroupPicker({ groups, value, placeholder, searchPlaceholder, emptyLabel, disabled = false, onSelect }) {
|
|
1303
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
1304
|
+
const [filter, setFilter] = (0, react.useState)("");
|
|
1305
|
+
const [activeIndex, setActiveIndex] = (0, react.useState)(0);
|
|
1306
|
+
const [triggerWidth, setTriggerWidth] = (0, react.useState)(0);
|
|
1307
|
+
const rootRef = (0, react.useRef)(null);
|
|
1308
|
+
const triggerRef = (0, react.useRef)(null);
|
|
1309
|
+
const panelRef = (0, react.useRef)(null);
|
|
1310
|
+
const listRef = (0, react.useRef)(null);
|
|
1311
|
+
const selected = value === "" ? undefined : groups.find((group) => group.id === value);
|
|
1312
|
+
const filtered = (0, react.useMemo)(() => {
|
|
1313
|
+
const needle = filter.trim().toLowerCase();
|
|
1314
|
+
if (needle === "") return groups;
|
|
1315
|
+
return groups.filter((group) => group.name.toLowerCase().includes(needle));
|
|
1316
|
+
}, [groups, filter]);
|
|
1317
|
+
const position = (0, __deepseek_ai_dsh_client_ui_primitives.useAnchoredPosition)({
|
|
1318
|
+
open,
|
|
1319
|
+
anchorRef: triggerRef,
|
|
1320
|
+
panelRef,
|
|
1321
|
+
side: "bottom",
|
|
1322
|
+
align: "start",
|
|
1323
|
+
gap: PANEL_GAP,
|
|
1324
|
+
margin: 24
|
|
1325
|
+
});
|
|
1326
|
+
const maxHeight = (0, __deepseek_ai_dsh_client_ui_primitives.useAnchoredMaxHeight)(panelRef, PANEL_MAX_HEIGHT, open ? filtered.length : 0);
|
|
1327
|
+
(0, __deepseek_ai_dsh_client_ui_primitives.useDismissOnOutsidePointer)(rootRef, open, setOpen, panelRef);
|
|
1328
|
+
(0, react.useEffect)(() => {
|
|
1329
|
+
if (!open) return;
|
|
1330
|
+
setFilter("");
|
|
1331
|
+
setActiveIndex(0);
|
|
1332
|
+
setTriggerWidth(triggerRef.current?.getBoundingClientRect().width ?? 0);
|
|
1333
|
+
const timer$1 = window.setTimeout(() => {
|
|
1334
|
+
panelRef.current?.querySelector("input")?.focus();
|
|
1335
|
+
}, 0);
|
|
1336
|
+
return () => window.clearTimeout(timer$1);
|
|
1337
|
+
}, [open]);
|
|
1338
|
+
(0, react.useEffect)(() => {
|
|
1339
|
+
if (!open) return;
|
|
1340
|
+
const onResize = () => {
|
|
1341
|
+
setTriggerWidth(triggerRef.current?.getBoundingClientRect().width ?? 0);
|
|
1342
|
+
};
|
|
1343
|
+
window.addEventListener("resize", onResize);
|
|
1344
|
+
return () => window.removeEventListener("resize", onResize);
|
|
1345
|
+
}, [open]);
|
|
1346
|
+
(0, react.useEffect)(() => {
|
|
1347
|
+
const list = listRef.current;
|
|
1348
|
+
if (list === null) return;
|
|
1349
|
+
list.querySelector("[data-active='true']")?.scrollIntoView({ block: "nearest" });
|
|
1350
|
+
}, [activeIndex, filter]);
|
|
1351
|
+
const close = () => {
|
|
1352
|
+
setOpen(false);
|
|
1353
|
+
triggerRef.current?.focus();
|
|
1354
|
+
};
|
|
1355
|
+
const pick = (group) => {
|
|
1356
|
+
onSelect(group);
|
|
1357
|
+
close();
|
|
1358
|
+
};
|
|
1359
|
+
const onKeyDown = (event) => {
|
|
1360
|
+
if (event.key === "Escape") {
|
|
1361
|
+
event.stopPropagation();
|
|
1362
|
+
close();
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
if (event.key === "ArrowDown") {
|
|
1366
|
+
event.preventDefault();
|
|
1367
|
+
if (filtered.length > 0) setActiveIndex((index) => Math.min(index + 1, filtered.length - 1));
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
if (event.key === "ArrowUp") {
|
|
1371
|
+
event.preventDefault();
|
|
1372
|
+
setActiveIndex((index) => Math.max(index - 1, 0));
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
if (event.key === "Enter") {
|
|
1376
|
+
event.preventDefault();
|
|
1377
|
+
const group = filtered[activeIndex];
|
|
1378
|
+
if (group !== undefined) pick(group);
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
1382
|
+
className: "s2s-groupPicker",
|
|
1383
|
+
ref: rootRef,
|
|
1384
|
+
children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
1385
|
+
type: "button",
|
|
1386
|
+
ref: triggerRef,
|
|
1387
|
+
className: "s2s-selectField",
|
|
1388
|
+
disabled,
|
|
1389
|
+
"aria-haspopup": "listbox",
|
|
1390
|
+
"aria-expanded": open,
|
|
1391
|
+
onClick: () => {
|
|
1392
|
+
setOpen((next) => !next);
|
|
1393
|
+
},
|
|
1394
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1395
|
+
className: selected === undefined ? "s2s-selectPlaceholder" : "s2s-selectValue",
|
|
1396
|
+
children: selected === undefined ? placeholder : selected.name
|
|
1397
|
+
}), (0, react_jsx_runtime.jsx)(__deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
|
|
1398
|
+
size: 14,
|
|
1399
|
+
className: `s2s-selectChevron${open ? " s2s-selectChevronOpen" : ""}`
|
|
1400
|
+
})]
|
|
1401
|
+
}), open && (0, react_jsx_runtime.jsxs)(__deepseek_ai_dsh_client_ui_primitives.MenuSurface, {
|
|
1402
|
+
ref: panelRef,
|
|
1403
|
+
className: "s2s-groupPanel",
|
|
1404
|
+
role: "listbox",
|
|
1405
|
+
style: position === null ? { visibility: "hidden" } : {
|
|
1406
|
+
...position,
|
|
1407
|
+
...triggerWidth > 0 ? { width: `${triggerWidth}px` } : {},
|
|
1408
|
+
maxHeight: `${maxHeight}px`
|
|
1409
|
+
},
|
|
1410
|
+
onKeyDown,
|
|
1411
|
+
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
1412
|
+
className: "s2s-groupSearch",
|
|
1413
|
+
children: (0, react_jsx_runtime.jsx)(__deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
1414
|
+
type: "text",
|
|
1415
|
+
value: filter,
|
|
1416
|
+
placeholder: searchPlaceholder,
|
|
1417
|
+
onChange: (event) => {
|
|
1418
|
+
setFilter(event.target.value);
|
|
1419
|
+
setActiveIndex(0);
|
|
1420
|
+
}
|
|
1421
|
+
})
|
|
1422
|
+
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
1423
|
+
className: "s2s-groupList",
|
|
1424
|
+
ref: listRef,
|
|
1425
|
+
children: [filtered.length === 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
1426
|
+
className: "s2s-groupEmpty",
|
|
1427
|
+
children: emptyLabel
|
|
1428
|
+
}), filtered.map((group, index) => (0, react_jsx_runtime.jsxs)("button", {
|
|
1429
|
+
type: "button",
|
|
1430
|
+
role: "option",
|
|
1431
|
+
"aria-selected": group.id === value,
|
|
1432
|
+
"data-active": index === activeIndex || undefined,
|
|
1433
|
+
className: "s2s-groupOption",
|
|
1434
|
+
onMouseEnter: () => {
|
|
1435
|
+
setActiveIndex(index);
|
|
1436
|
+
},
|
|
1437
|
+
onClick: () => {
|
|
1438
|
+
pick(group);
|
|
1439
|
+
},
|
|
1440
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1441
|
+
className: "s2s-groupOptionName",
|
|
1442
|
+
children: group.name
|
|
1443
|
+
}), group.id === value && (0, react_jsx_runtime.jsx)("span", {
|
|
1444
|
+
className: "s2s-groupOptionCheck",
|
|
1445
|
+
"aria-hidden": "true",
|
|
1446
|
+
children: "✓"
|
|
1447
|
+
})]
|
|
1448
|
+
}, group.id))]
|
|
1449
|
+
})]
|
|
1450
|
+
})]
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1187
1454
|
//#endregion
|
|
1188
1455
|
//#region src/client/section/KeysCard.tsx
|
|
1189
1456
|
const PAGE_SIZE = 5;
|
|
@@ -1200,7 +1467,6 @@ function KeysCard({ api, config, groups, t }) {
|
|
|
1200
1467
|
const [loading, setLoading] = (0, react.useState)(false);
|
|
1201
1468
|
const [listError, setListError] = (0, react.useState)(undefined);
|
|
1202
1469
|
const [creating, setCreating] = (0, react.useState)(false);
|
|
1203
|
-
const [groupMenuOpen, setGroupMenuOpen] = (0, react.useState)(false);
|
|
1204
1470
|
const [groupId, setGroupId] = (0, react.useState)("");
|
|
1205
1471
|
const [name$1, setName] = (0, react.useState)("");
|
|
1206
1472
|
const [nameTouched, setNameTouched] = (0, react.useState)(false);
|
|
@@ -1279,6 +1545,9 @@ else nextSet.delete(keyId);
|
|
|
1279
1545
|
setGroupId(gid);
|
|
1280
1546
|
if (!nameTouched) setName(groupNameOf(gid));
|
|
1281
1547
|
};
|
|
1548
|
+
const selectGroup = (group) => {
|
|
1549
|
+
pickGroup(group.id);
|
|
1550
|
+
};
|
|
1282
1551
|
const closeCreate = () => {
|
|
1283
1552
|
setCreating(false);
|
|
1284
1553
|
setGroupId("");
|
|
@@ -1467,36 +1736,14 @@ else nextSet.delete(keyId);
|
|
|
1467
1736
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1468
1737
|
className: "s2s-fieldLabel",
|
|
1469
1738
|
children: t("keyGroupLabel")
|
|
1470
|
-
}), (0, react_jsx_runtime.jsx)(
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
},
|
|
1479
|
-
children: (0, react_jsx_runtime.jsxs)("span", {
|
|
1480
|
-
className: "s2s-selectTrigger",
|
|
1481
|
-
children: [(0, react_jsx_runtime.jsx)("span", { children: groupId === "" ? t("keyGroupPlaceholder") : groupNameOf(groupId) }), (0, react_jsx_runtime.jsx)(__deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
|
|
1482
|
-
size: 14,
|
|
1483
|
-
className: "s2s-selectChevron"
|
|
1484
|
-
})]
|
|
1485
|
-
})
|
|
1486
|
-
})
|
|
1487
|
-
}),
|
|
1488
|
-
items: groups.map((group) => ({
|
|
1489
|
-
id: String(group.id),
|
|
1490
|
-
label: group.name
|
|
1491
|
-
})),
|
|
1492
|
-
selectedId: groupId === "" ? undefined : String(groupId),
|
|
1493
|
-
onSelect: (id) => {
|
|
1494
|
-
pickGroup(Number(id));
|
|
1495
|
-
setGroupMenuOpen(false);
|
|
1496
|
-
},
|
|
1497
|
-
onClose: () => {
|
|
1498
|
-
setGroupMenuOpen(false);
|
|
1499
|
-
}
|
|
1739
|
+
}), (0, react_jsx_runtime.jsx)(GroupPicker, {
|
|
1740
|
+
groups,
|
|
1741
|
+
value: groupId,
|
|
1742
|
+
placeholder: t("keyGroupPlaceholder"),
|
|
1743
|
+
searchPlaceholder: t("keyGroupSearch"),
|
|
1744
|
+
emptyLabel: t("keyGroupSearchEmpty"),
|
|
1745
|
+
disabled: busy,
|
|
1746
|
+
onSelect: selectGroup
|
|
1500
1747
|
})]
|
|
1501
1748
|
}), (0, react_jsx_runtime.jsxs)("div", {
|
|
1502
1749
|
className: "s2s-inline",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-sub2api-sync",
|
|
3
3
|
"description": "Sync one sub2api instance's groups, keys, and models into dsh-llm-pi-ai provider routes: panel-driven login (password / OAuth), one relay key one provider.",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.5",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Shuai-S",
|
|
7
7
|
"homepage": "https://github.com/Shuai-S/dsh-sub2api-sync#readme",
|