@noob-stupid/dsh-plugin-console 0.5.92 → 0.5.94
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 +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +1097 -70
- package/lib/server/domain/plugin-annotations.js +569 -0
- package/lib/server/domain/update-batch.js +181 -0
- package/lib/server/routes/annotations.js +82 -0
- package/lib/server/routes/framework.js +137 -120
- package/lib/server/routes/index.js +12 -0
- package/lib/server/routes/update-batch.js +97 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
window.__ModuleLoader__.load({
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
2
|
id: "@noob-stupid/dsh-plugin-console",
|
|
3
3
|
factory: (require) => {
|
|
4
4
|
var module = { exports: {} };
|
|
@@ -6,7 +6,7 @@ window.__ModuleLoader__.load({
|
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
8
|
//#region styles
|
|
9
|
-
const css = ".pc_section{width:100%;max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:14px;display:flex}.pc_section h3{margin:0;font-size:13px;font-weight:600;line-height:20px}.pc_message{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px;margin:0}.pc_messageRow{display:flex;align-items:center;gap:10px}.pc_spinner{width:14px;height:14px;border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-state-business-primary);border-radius:50%;animation:pcspin 1s linear infinite;flex:none}@keyframes pcspin{to{transform:rotate(360deg)}}.pc_message[data-error=true]{color:var(--dsw-alias-state-error-primary)}.pc_message[data-warn=true]{color:var(--dsw-alias-state-warning-primary)}.pc_list{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:0;padding:0;list-style:none;display:grid}.pc_row{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_rowTop{align-items:center;gap:8px;display:flex}.pc_name{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.pc_tag{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:16px;flex:none}.pc_tag[data-enabled=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_tag[data-user=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_tag[data-pending=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_status[data-pending=true]{color:var(--dsw-alias-state-warning-primary)}.pc_toggle[data-pending=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_tag[data-skill=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_tag[data-system=true]{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2)}.pc_tag[data-disabled=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_tag[data-suite=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_tag[data-error=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_tabGroup{display:inline-flex;gap:4px;align-items:center}.pc_meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;gap:6px;display:flex;align-items:center}.pc_phase[data-phase=failed]{color:var(--dsw-alias-state-error-primary)}.pc_phase[data-phase=active]{color:var(--dsw-alias-state-success-primary)}.pc_toggle{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:4px 12px;align-self:flex-start}.pc_toggle:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary)}.pc_toggle:disabled{opacity:.5;cursor:default}.pc_search{gap:8px;display:flex}.pc_search input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);height:34px;flex:1;color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;outline:none;padding:0 12px;font-size:13px}.pc_search input:focus-visible{border-color:var(--dsw-alias-state-business-primary)}.pc_search button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:0 14px;font-size:13px}.pc_search button:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_market{flex-direction:column;gap:10px;margin:0;padding:0;list-style:none;display:flex}.pc_item{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_itemTop{align-items:center;gap:8px;display:flex}.pc_itemTop a{color:var(--dsw-alias-state-business-primary);font-size:13px;font-weight:600;text-decoration:none;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pc_stars{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none}.pc_desc{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0}.pc_item button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:3px 10px;align-self:flex-start;font-size:12px}.pc_item button:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary)}.pc_item button:disabled{opacity:.5;cursor:default}.pc_detail{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_status{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0}.pc_note{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;margin:0}.pc_note[data-truncated=true]{color:var(--dsw-alias-state-warning-primary)}.pc_marketHead{display:flex;align-items:center;justify-content:space-between;gap:10px}.pc_descWrap{flex-direction:column;display:flex}.pc_installedHead{display:flex;align-items:center;gap:10px}.pc_backTopWrap{position:fixed;top:56px;right:20px;z-index:100;display:flex;flex-direction:column;gap:8px}.pc_backTop{width:34px;height:34px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:rgba(22,27,34,.72);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);cursor:pointer;backdrop-filter:blur(4px);opacity:.7}.pc_backTop:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_descTopbar{display:flex;justify-content:flex-end;margin:2px 0}.pc_ghpill{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 10px;font-size:11px;line-height:18px}.pc_ghpill[data-on=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_extraBtn{opacity:.75;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:4px 10px;font-size:12px;line-height:16px;flex:none;transition:opacity .15s ease,border-color 1.5s ease,color 1.5s ease}.pc_extraBtn:hover{opacity:1}.pc_extraBtn[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_extraBtn[data-flash=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_view{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:2px 10px;font-size:12px;line-height:18px;flex:none}.pc_floatPanel{position:fixed;left:20px;top:50%;transform:translateY(-50%);width:340px;max-height:75vh;overflow-y:auto;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);z-index:50;flex-direction:column;gap:8px;display:flex}.pc_refresh{position:sticky;bottom:14px;align-self:flex-end;margin-top:8px;z-index:60;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_refresh:hover{opacity:.9}.pc_date{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;flex:none}.pc_searchInner{position:relative;flex:1;display:flex;align-items:center;min-width:0}.pc_searchInner input{padding-right:32px;width:100%}.pc_starBtn{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:13px;line-height:1;padding:4px 8px;border-radius:6px;opacity:.75;transition:opacity .15s ease;flex:none}.pc_starBtn:hover{opacity:1}.pc_starBtn[data-active=true]{opacity:1;color:var(--dsw-alias-state-business-primary)}.pc_restartBtn{position:fixed;right:20px;top:50%;transform:translateY(-50%);z-index:60;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_restartBtn:hover{opacity:.9}.pc_relaunchFloat{position:fixed;left:20px;top:50%;transform:translateY(-50%);z-index:70;opacity:.85;border:1px solid var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_relaunchFloat:hover{opacity:1}.pc_aiToggle{position:fixed;right:20px;top:20px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_aiToggle:hover{opacity:1}.pc_aiToggle[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_sourcesFloat{position:fixed;right:20px;top:104px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_sourcesFloat:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_modeFloat{position:fixed;right:20px;top:204px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_modeFloat:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_modeFloat[data-mode=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_trashBtn{border:0;background:0 0;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:12px;line-height:16px;padding:2px 6px;flex:none;display:flex;align-items:center;border-radius:6px;transition:color .15s ease}.pc_trashBtn:hover{color:var(--dsw-alias-state-error-primary)}.pc_consentRemember{display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;color:var(--dsw-alias-label-secondary);cursor:pointer}.pc_modalBackdrop{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:1000;display:flex;align-items:center;justify-content:center}.pc_modalCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:12px;padding:16px 18px;width:min(760px,calc(100vw - 64px));max-height:calc(100vh - 96px);overflow-y:auto;overscroll-behavior:contain;flex-direction:column;gap:10px;display:flex;box-shadow:0 12px 32px rgba(0,0,0,.25)}.pc_modalCard::-webkit-scrollbar{width:8px}.pc_modalCard::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l2);border-radius:4px}.pc_srcUrl{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%}.pc_accWrap{display:flex;flex-direction:column;gap:8px}.pc_acc{border:1px solid var(--dsw-alias-border-l2);border-radius:10px;overflow:hidden;background:var(--dsw-alias-bg-layer-2)}.pc_accHead{width:100%;text-align:left;background:transparent;border:0;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;font-weight:600;line-height:20px;padding:8px 12px;cursor:pointer;display:flex;align-items:center;gap:6px}.pc_accHead:hover{background:var(--dsw-alias-bg-layer-3)}.pc_accHead[data-open=true]{border-bottom:1px solid var(--dsw-alias-border-l2)}.pc_accBody{padding:10px 12px;display:flex;flex-direction:column;gap:8px}.pc_ghpill{background:0 0;cursor:pointer}.pc_ghwrap{position:relative;display:inline-flex}.pc_sourceMenu{position:absolute;right:0;top:calc(100% + 6px);z-index:90;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;padding:4px;flex-direction:column;display:flex;min-width:120px;box-shadow:0 8px 24px rgba(0,0,0,.18)}.pc_sourceOpt{border:0;background:0 0;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:6px 10px;text-align:left;font-size:12px;line-height:16px}.pc_sourceOpt[data-active=true]{color:var(--dsw-alias-state-business-primary);font-weight:600}.pc_sourceOpt:hover{background:var(--dsw-alias-bg-layer-3)}.pc_aiEmpower{position:fixed;right:20px;top:48px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_aiEmpower:hover{opacity:.9}.pc_aiEmpower[data-busy=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_aiEmpower[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_compCard{position:fixed;left:8px;top:20px;width:320px;max-height:70vh;overflow-y:auto;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);z-index:45;flex-direction:column;gap:8px;display:flex}.pc_compCard .pc_rowTop{flex-wrap:wrap}.pc_compCard .pc_toggle{padding:3px 8px;font-size:11px}.pc_compCard[data-hidden=true]{display:none}.pc_compDrop{background:rgba(24,28,34,.92);backdrop-filter:blur(6px);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:4px;box-shadow:0 8px 24px rgba(0,0,0,.35);flex-direction:column;display:flex;gap:2px}.pc_compDropRow{border:0;background:0 0;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:6px 8px;text-align:left;font-size:12px;line-height:16px;flex-direction:column;display:flex;gap:2px}.pc_compDropRow:hover{background:var(--dsw-alias-bg-layer-2)}.pc_compDropUrl{color:var(--dsw-alias-label-tertiary);font-size:11px;word-break:break-all}.pc_compToggle{position:fixed;left:336px;top:20px;z-index:46;opacity:.55;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:rgba(22,27,34,.72);font:inherit;cursor:pointer;width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;line-height:1;backdrop-filter:blur(4px);transition:opacity .15s ease}.pc_compToggle:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_toolbarMain{position:fixed;right:70px;top:max(88px,21vh);z-index:71;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:border-color .15s ease}.pc_toolbarMain:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_toolbarMain[data-active=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_toolbarGroup{position:fixed;right:70px;top:calc(max(88px,21vh) + 36px);z-index:70;flex-direction:column;gap:8px;display:flex;transform-origin:top right;animation:pcDrawerIn .3s cubic-bezier(.34,1.56,.64,1)}@media (max-width:1160px){.pc_toolbarMain{top:max(150px,30vh)}.pc_toolbarGroup{top:calc(max(150px,30vh) + 36px)}.pc_aiToggle{top:104px}.pc_aiEmpower{top:132px}.pc_sourcesFloat{top:188px}.pc_modeFloat{top:288px}}@keyframes pcDrawerIn{from{opacity:0;transform:translateX(28px) scale(.94)}to{opacity:1;transform:translateX(0) scale(1)}}.pc_toolItem{opacity:.85;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_toolItem:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_toolItem[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_toolItem[data-mode=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_switch{width:34px;height:18px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);position:relative;cursor:pointer;flex:none;transition:background .15s ease,border-color .15s ease}.pc_switch:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_switch[data-on=true]{background:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.pc_switchKnob{position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:#fff;transition:left .15s ease}.pc_switch[data-on=true] .pc_switchKnob{left:18px}.pc_aiSpinner{width:12px;height:12px;border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-state-business-primary);border-radius:50%;animation:pcspin 1s linear infinite;flex:none}.pc_aiSpinBtn{position:fixed;right:152px;top:20px;z-index:71;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px}.pc_aiSpinBtn:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_tag[data-bad=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_progress{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;margin:0;font-variant-numeric:tabular-nums}.pc_consentCard{border:2px solid var(--dsw-alias-state-warning-primary);background:var(--dsw-alias-bg-layer-2);border-radius:10px;padding:12px 14px;flex-direction:column;gap:8px;display:flex;box-shadow:0 0 0 3px rgba(255,176,32,.10)}.pc_consentCountdown{margin-left:auto;flex:none;font-size:14px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--dsw-alias-state-warning-primary)}.pc_consentApprove{border:1px solid var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary);background:0 0;font:inherit;font-weight:600;cursor:pointer;border-radius:6px;padding:4px 12px;align-self:flex-start}.pc_consentApprove:hover{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_starFoot{margin-top:auto;position:sticky;bottom:0;justify-content:flex-end;display:flex;padding:6px 0 2px;background:var(--dsw-alias-bg-layer-1);box-shadow:0 16px 0 var(--dsw-alias-bg-layer-1)}.pc_starLink{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:12px;line-height:16px;text-decoration:none;display:inline-flex;flex:none;border-radius:4px}.pc_starLink svg{flex:none;display:block}.pc_starLink:hover{color:var(--dsw-alias-state-business-primary);text-decoration:underline;text-underline-offset:3px}.pc_starLink:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}";
|
|
9
|
+
const css = ".pc_section{width:100%;max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:14px;display:flex}.pc_section h3{margin:0;font-size:13px;font-weight:600;line-height:20px}.pc_message{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px;margin:0}.pc_messageRow{display:flex;align-items:center;gap:10px}.pc_spinner{width:14px;height:14px;border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-state-business-primary);border-radius:50%;animation:pcspin 1s linear infinite;flex:none}@keyframes pcspin{to{transform:rotate(360deg)}}.pc_message[data-error=true]{color:var(--dsw-alias-state-error-primary)}.pc_message[data-warn=true]{color:var(--dsw-alias-state-warning-primary)}.pc_list{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:0;padding:0;list-style:none;display:grid}.pc_row{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_rowTop{align-items:center;gap:8px;display:flex}.pc_name{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.pc_tag{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:16px;flex:none}.pc_tag[data-enabled=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_tag[data-user=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_tag[data-pending=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_status[data-pending=true]{color:var(--dsw-alias-state-warning-primary)}.pc_toggle[data-pending=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_tag[data-skill=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_tag[data-system=true]{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2)}.pc_tag[data-disabled=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_tag[data-suite=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_tag[data-error=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_tabGroup{display:inline-flex;gap:4px;align-items:center}.pc_meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;gap:6px;display:flex;align-items:center}.pc_phase[data-phase=failed]{color:var(--dsw-alias-state-error-primary)}.pc_phase[data-phase=active]{color:var(--dsw-alias-state-success-primary)}.pc_toggle{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:4px 12px;align-self:flex-start}.pc_toggle:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary)}.pc_toggle:disabled{opacity:.5;cursor:default}.pc_search{gap:8px;display:flex}.pc_search input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);height:34px;flex:1;color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;outline:none;padding:0 12px;font-size:13px}.pc_search input:focus-visible{border-color:var(--dsw-alias-state-business-primary)}.pc_search button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:0 14px;font-size:13px}.pc_search button:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_market{flex-direction:column;gap:10px;margin:0;padding:0;list-style:none;display:flex}.pc_item{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_itemTop{align-items:center;gap:8px;display:flex}.pc_itemTop a{color:var(--dsw-alias-state-business-primary);font-size:13px;font-weight:600;text-decoration:none;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pc_stars{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none}.pc_desc{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0}.pc_item button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:3px 10px;align-self:flex-start;font-size:12px}.pc_item button:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary)}.pc_item button:disabled{opacity:.5;cursor:default}.pc_detail{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_status{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0}.pc_note{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;margin:0}.pc_note[data-truncated=true]{color:var(--dsw-alias-state-warning-primary)}.pc_marketHead{display:flex;align-items:center;justify-content:space-between;gap:10px}.pc_descWrap{flex-direction:column;display:flex}.pc_installedHead{display:flex;align-items:center;gap:10px}.pc_iconBtn{width:26px;height:26px;padding:0;display:flex;align-items:center;justify-content:center;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;background:0 0;color:var(--dsw-alias-label-primary);cursor:pointer;flex:none}.pc_iconBtn:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary)}.pc_iconBtn:disabled{opacity:.5;cursor:default}.pc_iconBtn[data-state=done]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_iconBtn[data-state=active]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-bg-layer-2)}.pc_badgeRow{display:flex;align-items:center;gap:4px;min-width:0;overflow:hidden;flex-wrap:nowrap;flex:0 1 auto;max-width:60%;height:20px;white-space:nowrap}.pc_badgeAll{display:flex;flex-wrap:wrap;gap:4px;margin-top:2px}.pc_tag[data-user-badge=true]{border-style:dashed;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-bg-layer-2)}.pc_tag[data-more=true]{border-style:dotted;cursor:pointer;font:inherit;background:0 0}.pc_userNote{border-left:2px solid var(--dsw-alias-state-business-primary);padding-left:8px;display:flex;flex-direction:column;gap:4px;margin-top:2px}.pc_noteInput{width:100%;font:inherit;padding:4px 6px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);resize:vertical}.pc_annotRow{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.pc_backTopWrap{position:fixed;top:56px;right:20px;z-index:100;display:flex;flex-direction:column;gap:8px}.pc_backTop{width:34px;height:34px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:rgba(22,27,34,.72);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);cursor:pointer;backdrop-filter:blur(4px);opacity:.7}.pc_backTop:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_descTopbar{display:flex;justify-content:flex-end;margin:2px 0}.pc_ghpill{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 10px;font-size:11px;line-height:18px}.pc_ghpill[data-on=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_extraBtn{opacity:.75;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:4px 10px;font-size:12px;line-height:16px;flex:none;transition:opacity .15s ease,border-color 1.5s ease,color 1.5s ease}.pc_extraBtn:hover{opacity:1}.pc_extraBtn[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_extraBtn[data-flash=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_view{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:2px 10px;font-size:12px;line-height:18px;flex:none}.pc_floatPanel{position:fixed;left:20px;top:50%;transform:translateY(-50%);width:340px;max-height:75vh;overflow-y:auto;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);z-index:50;flex-direction:column;gap:8px;display:flex}.pc_refresh{position:sticky;bottom:14px;align-self:flex-end;margin-top:8px;z-index:60;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_refresh:hover{opacity:.9}.pc_date{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;flex:none}.pc_searchInner{position:relative;flex:1;display:flex;align-items:center;min-width:0}.pc_searchInner input{padding-right:32px;width:100%}.pc_starBtn{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:13px;line-height:1;padding:4px 8px;border-radius:6px;opacity:.75;transition:opacity .15s ease;flex:none}.pc_starBtn:hover{opacity:1}.pc_starBtn[data-active=true]{opacity:1;color:var(--dsw-alias-state-business-primary)}.pc_restartBtn{position:fixed;right:20px;top:50%;transform:translateY(-50%);z-index:60;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_restartBtn:hover{opacity:.9}.pc_relaunchFloat{position:fixed;left:20px;top:50%;transform:translateY(-50%);z-index:70;opacity:.85;border:1px solid var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_relaunchFloat:hover{opacity:1}.pc_aiToggle{position:fixed;right:20px;top:20px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_aiToggle:hover{opacity:1}.pc_aiToggle[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_sourcesFloat{position:fixed;right:20px;top:104px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_sourcesFloat:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_modeFloat{position:fixed;right:20px;top:204px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_modeFloat:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_modeFloat[data-mode=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_trashBtn{border:0;background:0 0;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:12px;line-height:16px;padding:2px 6px;flex:none;display:flex;align-items:center;border-radius:6px;transition:color .15s ease}.pc_trashBtn:hover{color:var(--dsw-alias-state-error-primary)}.pc_consentRemember{display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;color:var(--dsw-alias-label-secondary);cursor:pointer}.pc_modalBackdrop{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:1000;display:flex;align-items:center;justify-content:center}.pc_modalCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:12px;padding:16px 18px;width:min(760px,calc(100vw - 64px));max-height:calc(100vh - 96px);overflow-y:auto;overscroll-behavior:contain;flex-direction:column;gap:10px;display:flex;box-shadow:0 12px 32px rgba(0,0,0,.25)}.pc_modalCard::-webkit-scrollbar{width:8px}.pc_modalCard::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l2);border-radius:4px}.pc_srcUrl{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%}.pc_accWrap{display:flex;flex-direction:column;gap:8px}.pc_acc{border:1px solid var(--dsw-alias-border-l2);border-radius:10px;overflow:hidden;background:var(--dsw-alias-bg-layer-2)}.pc_accHead{width:100%;text-align:left;background:transparent;border:0;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;font-weight:600;line-height:20px;padding:8px 12px;cursor:pointer;display:flex;align-items:center;gap:6px}.pc_accHead:hover{background:var(--dsw-alias-bg-layer-3)}.pc_accHead[data-open=true]{border-bottom:1px solid var(--dsw-alias-border-l2)}.pc_accBody{padding:10px 12px;display:flex;flex-direction:column;gap:8px}.pc_ghpill{background:0 0;cursor:pointer}.pc_ghwrap{position:relative;display:inline-flex}.pc_sourceMenu{position:absolute;right:0;top:calc(100% + 6px);z-index:90;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;padding:4px;flex-direction:column;display:flex;min-width:120px;box-shadow:0 8px 24px rgba(0,0,0,.18)}.pc_sourceOpt{border:0;background:0 0;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:6px 10px;text-align:left;font-size:12px;line-height:16px}.pc_sourceOpt[data-active=true]{color:var(--dsw-alias-state-business-primary);font-weight:600}.pc_sourceOpt:hover{background:var(--dsw-alias-bg-layer-3)}.pc_aiEmpower{position:fixed;right:20px;top:48px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_aiEmpower:hover{opacity:.9}.pc_aiEmpower[data-busy=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_aiEmpower[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_compCard{position:fixed;left:8px;top:20px;width:320px;max-height:70vh;overflow-y:auto;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);z-index:45;flex-direction:column;gap:8px;display:flex}.pc_compCard .pc_rowTop{flex-wrap:wrap}.pc_compCard .pc_toggle{padding:3px 8px;font-size:11px}.pc_compCard[data-hidden=true]{display:none}.pc_compDrop{background:rgba(24,28,34,.92);backdrop-filter:blur(6px);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:4px;box-shadow:0 8px 24px rgba(0,0,0,.35);flex-direction:column;display:flex;gap:2px}.pc_compDropRow{border:0;background:0 0;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:6px 8px;text-align:left;font-size:12px;line-height:16px;flex-direction:column;display:flex;gap:2px}.pc_compDropRow:hover{background:var(--dsw-alias-bg-layer-2)}.pc_compDropUrl{color:var(--dsw-alias-label-tertiary);font-size:11px;word-break:break-all}.pc_compToggle{position:fixed;left:336px;top:20px;z-index:46;opacity:.55;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:rgba(22,27,34,.72);font:inherit;cursor:pointer;width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;line-height:1;backdrop-filter:blur(4px);transition:opacity .15s ease}.pc_compToggle:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_toolbarMain{position:fixed;right:70px;top:max(88px,21vh);z-index:71;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:border-color .15s ease}.pc_toolbarMain:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_toolbarMain[data-active=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_toolbarGroup{position:fixed;right:70px;top:calc(max(88px,21vh) + 36px);z-index:70;flex-direction:column;gap:8px;display:flex;transform-origin:top right;animation:pcDrawerIn .3s cubic-bezier(.34,1.56,.64,1)}@media (max-width:1160px){.pc_toolbarMain{top:max(150px,30vh)}.pc_toolbarGroup{top:calc(max(150px,30vh) + 36px)}.pc_aiToggle{top:104px}.pc_aiEmpower{top:132px}.pc_sourcesFloat{top:188px}.pc_modeFloat{top:288px}}@keyframes pcDrawerIn{from{opacity:0;transform:translateX(28px) scale(.94)}to{opacity:1;transform:translateX(0) scale(1)}}.pc_toolItem{opacity:.85;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_toolItem:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_toolItem[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_toolItem[data-mode=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_switch{width:34px;height:18px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);position:relative;cursor:pointer;flex:none;transition:background .15s ease,border-color .15s ease}.pc_switch:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_switch[data-on=true]{background:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.pc_switchKnob{position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:#fff;transition:left .15s ease}.pc_switch[data-on=true] .pc_switchKnob{left:18px}.pc_aiSpinner{width:12px;height:12px;border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-state-business-primary);border-radius:50%;animation:pcspin 1s linear infinite;flex:none}.pc_aiSpinBtn{position:fixed;right:152px;top:20px;z-index:71;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px}.pc_aiSpinBtn:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_tag[data-bad=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_progress{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;margin:0;font-variant-numeric:tabular-nums}.pc_consentCard{border:2px solid var(--dsw-alias-state-warning-primary);background:var(--dsw-alias-bg-layer-2);border-radius:10px;padding:12px 14px;flex-direction:column;gap:8px;display:flex;box-shadow:0 0 0 3px rgba(255,176,32,.10)}.pc_consentCountdown{margin-left:auto;flex:none;font-size:14px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--dsw-alias-state-warning-primary)}.pc_consentApprove{border:1px solid var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary);background:0 0;font:inherit;font-weight:600;cursor:pointer;border-radius:6px;padding:4px 12px;align-self:flex-start}.pc_consentApprove:hover{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_starFoot{margin-top:auto;position:sticky;bottom:0;justify-content:flex-end;display:flex;padding:6px 0 2px;background:var(--dsw-alias-bg-layer-1);box-shadow:0 16px 0 var(--dsw-alias-bg-layer-1)}.pc_starLink{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:12px;line-height:16px;text-decoration:none;display:inline-flex;flex:none;border-radius:4px}.pc_starLink svg{flex:none;display:block}.pc_starLink:hover{color:var(--dsw-alias-state-business-primary);text-decoration:underline;text-underline-offset:3px}.pc_starLink:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.pc_groupNew{animation:pcGroupIn 260ms cubic-bezier(.2,.9,.3,1.25) both}@keyframes pcGroupIn{0%{opacity:0;transform:translateY(-10px) scale(.94)}55%{opacity:1;transform:translateY(4px) scale(1.03)}76%{transform:translateY(-2px) scale(.995)}100%{opacity:1;transform:none}}@media (prefers-reduced-motion:reduce){.pc_groupNew{animation:none}}";
|
|
10
10
|
const tagId = "@noob-stupid/dsh-plugin-console/PluginConsoleTab.css";
|
|
11
11
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
12
12
|
const tag = document.createElement("style");
|
|
@@ -87,6 +87,13 @@ window.__ModuleLoader__.load({
|
|
|
87
87
|
detail: "pc_detail",
|
|
88
88
|
status: "pc_status",
|
|
89
89
|
note: "pc_note",
|
|
90
|
+
iconBtn: "pc_iconBtn",
|
|
91
|
+
badgeRow: "pc_badgeRow",
|
|
92
|
+
badgeAll: "pc_badgeAll",
|
|
93
|
+
userNote: "pc_userNote",
|
|
94
|
+
noteInput: "pc_noteInput",
|
|
95
|
+
annotRow: "pc_annotRow",
|
|
96
|
+
groupNew: "pc_groupNew",
|
|
90
97
|
};
|
|
91
98
|
//#endregion
|
|
92
99
|
//#region locales
|
|
@@ -268,7 +275,87 @@ window.__ModuleLoader__.load({
|
|
|
268
275
|
familyCheckUpdate: "批量检测更新",
|
|
269
276
|
familyUpdateBtn: "更新",
|
|
270
277
|
familyCheckUpdateTip: "检测到新版本后此处出现「更新」按钮,点击开始整包更新(含子包版本对齐与适配校验)",
|
|
278
|
+
// issue #22 ①:已安装列表的「一键检测全部更新」(只有图标 ↑↑↑ 的圆按钮 + 结果/筛选态)
|
|
279
|
+
checkAllUpdates: "检测全部更新",
|
|
280
|
+
checkAllUpdatesHint: "一键检测全部已装插件是否有新版(本次要查 {n} 个)",
|
|
281
|
+
checkingAllUpdates: "正在检测全部更新({n} 个,并发受限,可能要几秒)",
|
|
282
|
+
checkAllUpdatesDone: "{n} 个可更新",
|
|
283
|
+
checkAllUpdateNone: "没有可更新的插件",
|
|
284
|
+
checkAllUpdateUnknownNote: "其中 {n} 个未检测到(网络或 registry 没答上来,可单独点卡片上的「检测更新」重试)",
|
|
285
|
+
checkAllUpdateFailed: "批量检测失败",
|
|
286
|
+
checkAllUpdateEmptyList: "已安装列表里没有可检测的插件",
|
|
287
|
+
batchUpdateOnly: "仅看可更新",
|
|
288
|
+
batchUpdateOnlyTip: "只显示本次检测到有更新的插件(再点一次取消)",
|
|
289
|
+
batchUpdateFilterEmpty: "本次检测没有可更新的插件(取消「仅看可更新」可看全部)",
|
|
290
|
+
batchUpdateAvailable: "可更新 → v{v}",
|
|
291
|
+
batchUpdateAvailableTip: "本次批量检测到新版本;点「更新」按这个版本更新(与单卡「检测更新」同一判据)",
|
|
292
|
+
batchUpToDate: "已最新",
|
|
293
|
+
batchUpdateUnknown: "未检测到",
|
|
294
|
+
batchUpdateUnknownTip: "registry 与 GitHub 都没答上来 ⇒ 不知道有没有新版(不是\"已是最新\");可单独点「检测更新」重试",
|
|
295
|
+
batchUpdateResultTip: "本次检测的结论:{n} 个可更新、{m} 个已最新、{k} 个未检测到({at})",
|
|
271
296
|
adaptUnlockBtn: "已适配,立即解锁",
|
|
297
|
+
// issue #22 ②③:用户注解(备注 / 用户徽章 / 用户自建分组)—— 都是**用户自己的数据**
|
|
298
|
+
annotationsBtn: "备注插件",
|
|
299
|
+
annotationsPanelTitle: "备注 / 用户徽章 / 用户分组:这些都是你自己的数据,存在 <DSH_HOME>/plugin-console/plugin-annotations.json,不写进插件、不改 profile;文件坏了也不影响上面的插件列表",
|
|
300
|
+
annotationsFailed: "注解操作失败",
|
|
301
|
+
annotationsCorrupt: "注解文件读不出来({err})—— 插件列表不受影响;修好或删掉该文件后再试写",
|
|
302
|
+
annotationsRepaired: "注解文件被手工改过,已按规则忽略 {n} 处不合规内容",
|
|
303
|
+
annotationsEmpty: "还没有任何备注 / 徽章 / 分组:在下面任一插件行里写第一条",
|
|
304
|
+
annotationsStats: "{p} 个插件有注解 · {b} 个用户徽章 · {g} 个分组",
|
|
305
|
+
annotationsListShow: "插件列表({n})· 点此展开",
|
|
306
|
+
annotationsListHide: "插件列表({n})· 点此收起",
|
|
307
|
+
annotationsSearchPlaceholder: "搜索:包名 / rowId / 备注内容(边打边筛)",
|
|
308
|
+
annotationsNoMatch: "没有匹配的插件",
|
|
309
|
+
noteLabel: "备注",
|
|
310
|
+
noteEdit: "改备注",
|
|
311
|
+
noteEmpty: "+ 加备注",
|
|
312
|
+
notePlaceholder: "这是干什么的、为什么装它",
|
|
313
|
+
noteSaveHint: "回车保存 · Shift+回车换行 · Esc 取消(存空 = 清空备注)",
|
|
314
|
+
noteSaved: "备注已保存",
|
|
315
|
+
noteCleared: "备注已清空",
|
|
316
|
+
badgeLabel: "用户徽章",
|
|
317
|
+
badgeAddPlaceholder: "徽章文字(回车添加)",
|
|
318
|
+
badgeAdded: "已加徽章「{b}」",
|
|
319
|
+
badgeRemove: "移除这个徽章",
|
|
320
|
+
badgeRemoved: "已移除徽章「{b}」",
|
|
321
|
+
badgeUndo: "撤销",
|
|
322
|
+
badgeUndoDone: "已恢复徽章",
|
|
323
|
+
badgeMax: "用户徽章最多 {n} 个",
|
|
324
|
+
badgesAll: "全部徽章({n}):系统徽章 + 用户徽章都在这",
|
|
325
|
+
groupLabel: "分组",
|
|
326
|
+
groupNone: "(不分组)",
|
|
327
|
+
groupCreate: "新建分组",
|
|
328
|
+
groupCreatePlaceholder: "分组名,如:记忆类(回车创建)",
|
|
329
|
+
groupCreated: "已创建分组「{name}」",
|
|
330
|
+
groupNewInlineHint: "在详情页直接建一个分组,并把当前插件放进去",
|
|
331
|
+
groupNewInlinePlaceholder: "分组名(回车创建并拉入)",
|
|
332
|
+
groupNewInline: "新建分组",
|
|
333
|
+
groupRename: "重命名",
|
|
334
|
+
groupRenamed: "分组已重命名",
|
|
335
|
+
groupRemove: "删除这个分组(只解除归属,不删插件)",
|
|
336
|
+
groupDeleteConfirm: "确认删除分组「{name}」?只解除归属,插件本身不会删",
|
|
337
|
+
groupDeleted: "已删除分组「{name}」(解除 {n} 个归属,插件本身没删)",
|
|
338
|
+
groupRestore: "撤销删除",
|
|
339
|
+
groupRestored: "分组「{name}」已恢复",
|
|
340
|
+
groupAssigned: "已加入分组「{name}」",
|
|
341
|
+
groupUnassigned: "已从分组移除",
|
|
342
|
+
groupMembers: "{n} 个插件",
|
|
343
|
+
groupNoMembers: "这个分组里还没有插件:从右边下拉里选一个加进来",
|
|
344
|
+
groupMemberPick: "+ 选一个插件加进这个分组",
|
|
345
|
+
groupUpdateAll: "一键更新本组",
|
|
346
|
+
groupUpdateAllTip: "按组内每个成员各自的目标版本逐个更新(串行,不并发;不适配的仍会被门控挡住)",
|
|
347
|
+
groupUpdateAllProgress: "正在更新第 {i}/{n} 个:",
|
|
348
|
+
groupUpdateAllDone: "本组更新完成:{n} 个成员,{hit} 个有新版并已发起更新",
|
|
349
|
+
groupLoadMore: "加载更多(还有 {n} 个)",
|
|
350
|
+
groupOpenHint: "点分组名可以在这里展开,搜索并添加插件",
|
|
351
|
+
groupAddSearchPlaceholder: "搜索插件名,点「加入」放进这个分组",
|
|
352
|
+
groupAddOne: "加入",
|
|
353
|
+
groupAddNoMatch: "没有匹配的插件(或都已在分组里)",
|
|
354
|
+
groupInFamilyNote: "成员都在官方全家桶区块里(同一插件不会重复显示)",
|
|
355
|
+
groupMemberNone: "插件都已经在这个分组里了",
|
|
356
|
+
groupMemberRemove: "从这个分组移出(只解除归属,不删插件)",
|
|
357
|
+
groupEmptyHint: "还没有分组:先在上面建一个(如「记忆类」),再到插件行里选它",
|
|
358
|
+
userGroupTag: "我的分组",
|
|
272
359
|
adaptUnlockHint: "已安装版本经源码扫描确认适配当前框架:立即移除禁用块并解锁启用",
|
|
273
360
|
adaptUnlockDone: "已适配解锁:刷新后该行可正常启用",
|
|
274
361
|
riskyEnableTitle: "强行启用不适配插件?",
|
|
@@ -441,7 +528,7 @@ window.__ModuleLoader__.load({
|
|
|
441
528
|
cfDone: "已改名 {renamed} / 重建链接 {linked} / 删备份 {removed} 条(释放约 {mb} MB)",
|
|
442
529
|
cfFailed: "{n} 项失败(详见 fw/console 日志)",
|
|
443
530
|
compatNoticeDismiss: "我知道了(此版本不再提示)",
|
|
444
|
-
searchInstalledPlaceholder: "
|
|
531
|
+
searchInstalledPlaceholder: "搜索已安装插件(名称 / id / 备注)",
|
|
445
532
|
extraFilter: "已装",
|
|
446
533
|
extraFilterAll: "全部",
|
|
447
534
|
extraTag: "第三方",
|
|
@@ -927,7 +1014,83 @@ window.__ModuleLoader__.load({
|
|
|
927
1014
|
familyCheckUpdate: "Check all",
|
|
928
1015
|
familyUpdateBtn: "Update",
|
|
929
1016
|
familyCheckUpdateTip: "When a newer version is detected this Update button appears; click to start the full-family update (subpackage alignment + adapt check)",
|
|
1017
|
+
// issue #22 ①: bulk update check for the installed list (icon-only ↑↑↑ button + result/filter states)
|
|
1018
|
+
checkAllUpdates: "Check all updates",
|
|
1019
|
+
checkAllUpdatesHint: "Check every installed plugin for a newer version ({n} to check)",
|
|
1020
|
+
checkingAllUpdates: "Checking all updates ({n} plugins, concurrency capped - may take a few seconds)",
|
|
1021
|
+
checkAllUpdatesDone: "{n} updatable",
|
|
1022
|
+
checkAllUpdateNone: "No updatable plugin",
|
|
1023
|
+
checkAllUpdateUnknownNote: "{n} could not be checked (network or registry did not answer; use the per-card \"Check update\" to retry)",
|
|
1024
|
+
checkAllUpdateFailed: "Bulk update check failed",
|
|
1025
|
+
checkAllUpdateEmptyList: "No checkable plugin in the installed list",
|
|
1026
|
+
batchUpdateOnly: "Updatable only",
|
|
1027
|
+
batchUpdateOnlyTip: "Show only plugins with an update detected in this check (click again to clear)",
|
|
1028
|
+
batchUpdateFilterEmpty: "No updatable plugin in this check (clear \"Updatable only\" to see all)",
|
|
1029
|
+
batchUpdateAvailable: "Update → v{v}",
|
|
1030
|
+
batchUpdateAvailableTip: "A newer version was detected by the bulk check; Update installs exactly this version (same criterion as the per-card Check update)",
|
|
1031
|
+
batchUpToDate: "Up to date",
|
|
1032
|
+
batchUpdateUnknown: "Not detected",
|
|
1033
|
+
batchUpdateUnknownTip: "Neither the registry nor GitHub answered, so \"no update\" is unknown - not \"up to date\". Retry per card with Check update",
|
|
1034
|
+
batchUpdateResultTip: "This check: {n} updatable, {m} up to date, {k} not detected ({at})",
|
|
930
1035
|
adaptUnlockBtn: "Adapted - unlock now",
|
|
1036
|
+
// issue #22 ②③: user annotations (notes / user badges / user groups) - the user's own data
|
|
1037
|
+
annotationsBtn: "Annotate",
|
|
1038
|
+
annotationsPanelTitle: "Notes / user badges / user groups: your own data, stored in <DSH_HOME>/plugin-console/plugin-annotations.json. Nothing is written into plugins or the profile, and a broken file never affects the plugin list above.",
|
|
1039
|
+
annotationsFailed: "Annotation action failed",
|
|
1040
|
+
annotationsCorrupt: "The annotation file cannot be read ({err}) - the plugin list is unaffected; fix or delete that file, then retry",
|
|
1041
|
+
annotationsRepaired: "The annotation file was edited by hand; {n} invalid entries were ignored",
|
|
1042
|
+
annotationsEmpty: "No notes / badges / groups yet: write the first one in any plugin row below",
|
|
1043
|
+
annotationsStats: "{p} annotated plugins · {b} user badges · {g} groups",
|
|
1044
|
+
annotationsListShow: "Plugin list ({n}) · click to expand",
|
|
1045
|
+
annotationsListHide: "Plugin list ({n}) · click to collapse",
|
|
1046
|
+
annotationsSearchPlaceholder: "Search: package name / rowId / note text (filters as you type)",
|
|
1047
|
+
annotationsNoMatch: "No matching plugins",
|
|
1048
|
+
noteLabel: "Note",
|
|
1049
|
+
noteEdit: "Edit note",
|
|
1050
|
+
noteEmpty: "+ Add note",
|
|
1051
|
+
notePlaceholder: "What is this plugin, and why did you install it",
|
|
1052
|
+
noteSaveHint: "Enter saves · Shift+Enter newline · Esc cancels (empty = clear the note)",
|
|
1053
|
+
noteSaved: "Note saved",
|
|
1054
|
+
noteCleared: "Note cleared",
|
|
1055
|
+
badgeLabel: "User badge",
|
|
1056
|
+
badgeAddPlaceholder: "Badge text (Enter to add)",
|
|
1057
|
+
badgeAdded: "Badge \"{b}\" added",
|
|
1058
|
+
badgeRemove: "Remove this badge",
|
|
1059
|
+
badgeRemoved: "Badge \"{b}\" removed",
|
|
1060
|
+
badgeUndo: "Undo",
|
|
1061
|
+
badgeUndoDone: "Badges restored",
|
|
1062
|
+
badgeMax: "At most {n} user badges",
|
|
1063
|
+
badgesAll: "All badges ({n}): system badges and user badges",
|
|
1064
|
+
groupLabel: "Group",
|
|
1065
|
+
groupNone: "(no group)",
|
|
1066
|
+
groupCreate: "New group",
|
|
1067
|
+
groupCreatePlaceholder: "Group name, e.g. Memory (Enter to create)",
|
|
1068
|
+
groupCreated: "Group \"{name}\" created",
|
|
1069
|
+
groupNewInlineHint: "Create a group here and put this plugin in it",
|
|
1070
|
+
groupNewInlinePlaceholder: "Group name (Enter creates and adds)",
|
|
1071
|
+
groupNewInline: "New group",
|
|
1072
|
+
groupRename: "Rename",
|
|
1073
|
+
groupRenamed: "Group renamed",
|
|
1074
|
+
groupRemove: "Delete this group (membership only, plugins are kept)",
|
|
1075
|
+
groupDeleteConfirm: "Delete group \"{name}\"? Only the membership is removed - plugins stay",
|
|
1076
|
+
groupDeleted: "Group \"{name}\" deleted ({n} memberships removed, plugins kept)",
|
|
1077
|
+
groupRestore: "Undo delete",
|
|
1078
|
+
groupRestored: "Group \"{name}\" restored",
|
|
1079
|
+
groupAssigned: "Added to group \"{name}\"",
|
|
1080
|
+
groupUnassigned: "Removed from the group",
|
|
1081
|
+
groupMembers: "{n} plugins",
|
|
1082
|
+
groupNoMembers: "No plugins in this group yet: pick one from the dropdown on the right",
|
|
1083
|
+
groupMemberPick: "+ Pick a plugin to add to this group",
|
|
1084
|
+
groupUpdateAll: "Update this group",
|
|
1085
|
+
groupUpdateAllTip: "Updates each member sequentially, one at a time, at its own target version; the compatibility gate still applies",
|
|
1086
|
+
groupUpdateAllProgress: "Updating {i}/{n}:",
|
|
1087
|
+
groupUpdateAllDone: "Group update finished: {n} members, {hit} had a newer version and were started",
|
|
1088
|
+
groupLoadMore: "Load more ({n} left)",
|
|
1089
|
+
groupInFamilyNote: "Members live inside the official family block, not duplicated here",
|
|
1090
|
+
groupMemberNone: "Every plugin is already in this group",
|
|
1091
|
+
groupMemberRemove: "Remove from this group (membership only, the plugin is kept)",
|
|
1092
|
+
groupEmptyHint: "No groups yet: create one above (e.g. Memory), then pick it in a plugin row",
|
|
1093
|
+
userGroupTag: "My group",
|
|
931
1094
|
adaptUnlockHint: "Installed version passed the source scan for the current framework: unlock immediately",
|
|
932
1095
|
adaptUnlockDone: "Unlocked: refresh and the row is enabled",
|
|
933
1096
|
riskyEnableTitle: "Force-enable an incompatible plugin?",
|
|
@@ -1088,7 +1251,7 @@ window.__ModuleLoader__.load({
|
|
|
1088
1251
|
githubLoginBtn: "Sign in",
|
|
1089
1252
|
githubLoginOk: "Signed in to GitHub: {login}",
|
|
1090
1253
|
githubLoginFail: "Sign-in failed",
|
|
1091
|
-
searchInstalledPlaceholder: "Search installed plugins (name
|
|
1254
|
+
searchInstalledPlaceholder: "Search installed plugins (name / id / note)",
|
|
1092
1255
|
extraFilter: "Installed",
|
|
1093
1256
|
extraFilterAll: "All",
|
|
1094
1257
|
extraTag: "Third-party",
|
|
@@ -1673,6 +1836,10 @@ window.__ModuleLoader__.load({
|
|
|
1673
1836
|
}
|
|
1674
1837
|
// <<<update-pick:end>>>
|
|
1675
1838
|
const el = react.createElement;
|
|
1839
|
+
// ★ 0.5.93(issue #22 ② 收口):徽章"放得下几个"的估算函数(BADGE_AREA_FALLBACK_PX / badgeChipWidthOf /
|
|
1840
|
+
// fitBadgeChunks / fitBadgeList)**整段删除**。它们配合 ResizeObserver 撑起了"卡片上收成 +N"那套机制,
|
|
1841
|
+
// 而用户定稿的行为是:卡片上放不下就**不显示**(纯 CSS 截断、无任何提示),全部徽章去详情页看。
|
|
1842
|
+
// 留着这套估算只会有人再把它接回去(抖动正是这么来的)。
|
|
1676
1843
|
// ★ 0.5.60:被跳过 bundle 的签名(包名+版本)。关掉提示时记下它;清单变了就重新出现。
|
|
1677
1844
|
// 模块级 —— 上一版把辅助函数放子组件里、主面板误用,导致整块面板空白。
|
|
1678
1845
|
function skippedSignature(skipped) {
|
|
@@ -2390,6 +2557,42 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
2390
2557
|
}, []);
|
|
2391
2558
|
// 自动版本比对结果:{ fullName: latest }(已安装且有新版)
|
|
2392
2559
|
const [updateMap, setUpdateMap] = react.useState({});
|
|
2560
|
+
// ★ issue #22 ①:已安装列表的「一键检测全部更新」(批量)。
|
|
2561
|
+
// 状态机:null(还没查过)→ checking(查中,带本次要查的包数)→ ready(有结论)/ error。
|
|
2562
|
+
// 界面只**展示**结论,不当判据:哪个版本更高由服务端唯一判据(`<<<update-pick>>>` 那块)给出,
|
|
2563
|
+
// 这里把 target 原样交给 startJob —— 与单卡「检测更新」同一个参数形状。
|
|
2564
|
+
const [batchCheck, setBatchCheck] = react.useState(null);
|
|
2565
|
+
// 「仅看可更新」筛选:只在 ready 且真的有可更新项时才渲染成按钮(没有可点的空状态)
|
|
2566
|
+
const [onlyUpdatable, setOnlyUpdatable] = react.useState(false);
|
|
2567
|
+
// ★ issue #22 ②③:用户注解(备注 / 用户徽章 / 用户自建分组)—— 独立文件、独立通道;
|
|
2568
|
+
// 读不到或写失败只影响这块,插件列表照常(服务端边界见 domain/plugin-annotations.js 顶部)。
|
|
2569
|
+
const [annotations, setAnnotations] = react.useState(null);
|
|
2570
|
+
const [annotationsNote, setAnnotationsNote] = react.useState(null);
|
|
2571
|
+
const [annotationBusy, setAnnotationBusy] = react.useState(null);
|
|
2572
|
+
const [annotationsPanelOpen, setAnnotationsPanelOpen] = react.useState(false);
|
|
2573
|
+
// ② 收口(0.5.93,用户定稿):面板一打开列表**收起**(只留一行摘要「插件列表(N)· 点此展开」);
|
|
2574
|
+
// 展开后可按 包名 / rowId / 已有备注内容 **边打边筛**(不需要点按钮)。
|
|
2575
|
+
// 查询词是受控 state —— 过滤过程绝不回写、更不清空用户正在输入的内容。
|
|
2576
|
+
const [annotationsListOpen, setAnnotationsListOpen] = react.useState(false);
|
|
2577
|
+
const [annotationsQuery, setAnnotationsQuery] = react.useState("");
|
|
2578
|
+
const [noteEdit, setNoteEdit] = react.useState(null);
|
|
2579
|
+
const [badgeDraft, setBadgeDraft] = react.useState({});
|
|
2580
|
+
const [badgeUndo, setBadgeUndo] = react.useState({});
|
|
2581
|
+
const [groupDraftName, setGroupDraftName] = react.useState("");
|
|
2582
|
+
// ③ 收口(0.5.93):**只**记住"刚刚建出来的那个分组 id"用来播一次入场动画;
|
|
2583
|
+
// 分组本身的真相永远来自服务端返回的全量 groups(annotationWrite 直接覆盖),
|
|
2584
|
+
// 这里不维护第二份客户端状态。
|
|
2585
|
+
const [groupJustCreated, setGroupJustCreated] = react.useState(null);
|
|
2586
|
+
// #5:详情页里就地新建分组 —— **按卡片 key 区分**({ key, draft } | null)。
|
|
2587
|
+
// 用一个全局值会让所有卡片同时展开输入框(用户实测到的问题)。
|
|
2588
|
+
const [detailGroupDraft, setDetailGroupDraft] = react.useState(null);
|
|
2589
|
+
// 面板里"点开某个分组"(只允许一个展开)+ 该分组内的搜索词
|
|
2590
|
+
const [panelGroupOpen, setPanelGroupOpen] = react.useState(null);
|
|
2591
|
+
const [panelGroupQuery, setPanelGroupQuery] = react.useState("");
|
|
2592
|
+
const [panelGroupShow, setPanelGroupShow] = react.useState(8); // 候选默认 8 个,可加载更多
|
|
2593
|
+
const [groupRenameEdit, setGroupRenameEdit] = react.useState(null);
|
|
2594
|
+
const [groupDeleteArm, setGroupDeleteArm] = react.useState(null);
|
|
2595
|
+
const [groupUndo, setGroupUndo] = react.useState(null);
|
|
2393
2596
|
const [expandedDescs, setExpandedDescs] = react.useState({});
|
|
2394
2597
|
const [installedQuery, setInstalledQuery] = react.useState(() => {
|
|
2395
2598
|
try { return localStorage.getItem("pc-market-installed-q") ?? ""; } catch { return ""; }
|
|
@@ -3216,6 +3419,68 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
3216
3419
|
(error) => setUpdateCheck({ status: "ready", latest: null, source: "npm", error: friendlyGithubError(error).message }),
|
|
3217
3420
|
);
|
|
3218
3421
|
};
|
|
3422
|
+
// ★ issue #22 ①:一键检测全部更新 —— **一次**请求查完已装列表(服务端并发上限 4,见 domain/update-batch.js)。
|
|
3423
|
+
// 为什么不是逐个包各发一个请求:那样并发由客户端决定,几十个包同时出去就是 0.5.84
|
|
3424
|
+
// 市场富化把网络打满、安装作业被拖死那次的形状(教训写在 domain/enrich-throttle.js 顶部)。
|
|
3425
|
+
// 为什么没有 x/y 进度:单个请求拿不到逐包进度;编一个假百分比比转圈更糟。要真进度只能分批请求
|
|
3426
|
+
// (每批一次请求、累加 x/y)——已写进 UI 方案等用户定,本版先给转圈 + 待查包数。
|
|
3427
|
+
const checkAllUpdates = () => {
|
|
3428
|
+
if (batchCheck !== null && batchCheck.status === "checking") return;
|
|
3429
|
+
const names = [];
|
|
3430
|
+
const seen = new Set();
|
|
3431
|
+
for (const entry of (state.status === "ready" ? state.data.entries : [])) {
|
|
3432
|
+
const mn = String(entry.moduleName ?? "");
|
|
3433
|
+
if (mn === "" || mn.startsWith("cordis:") || seen.has(mn)) continue;
|
|
3434
|
+
seen.add(mn);
|
|
3435
|
+
names.push(mn);
|
|
3436
|
+
}
|
|
3437
|
+
if (names.length === 0) {
|
|
3438
|
+
// 没有可查的东西也必须**有反馈**(空列表不是"点了没反应")
|
|
3439
|
+
setMessage(t("checkAllUpdateEmptyList"));
|
|
3440
|
+
return;
|
|
3441
|
+
}
|
|
3442
|
+
setBatchCheck({ status: "checking", total: names.length });
|
|
3443
|
+
setMessage(t("checkingAllUpdates").replace("{n}", String(names.length)));
|
|
3444
|
+
// ★ 2026-10-27:**分批**跑(每批 40,逐批回报进度)。整批一次性丢给服务端时,
|
|
3445
|
+
// 界面在几分钟里只有一个转圈,用户以为"卡在限制上"。分批后可看到"已查 40/238"逐步推进。
|
|
3446
|
+
const CHUNK = 40;
|
|
3447
|
+
const merged = { items: [], checked: 0, updatable: 0, unknown: 0 };
|
|
3448
|
+
const runChunk = (offset) => {
|
|
3449
|
+
const slice = names.slice(offset, offset + CHUNK);
|
|
3450
|
+
if (slice.length === 0) return Promise.resolve(merged);
|
|
3451
|
+
setMessage(t("checkingAllUpdates").replace("{n}", String(names.length)) + "(" + offset + "/" + names.length + ")");
|
|
3452
|
+
return call("/plugin-console/check-updates", { packageNames: slice }).then((data) => {
|
|
3453
|
+
if (data !== null && Array.isArray(data.items)) merged.items.push(...data.items);
|
|
3454
|
+
merged.checked += typeof data?.checked === "number" ? data.checked : slice.length;
|
|
3455
|
+
merged.updatable += typeof data?.updatable === "number" ? data.updatable : 0;
|
|
3456
|
+
merged.unknown += typeof data?.unknown === "number" ? data.unknown : 0;
|
|
3457
|
+
setBatchCheck({ status: "checking", total: names.length, done: merged.checked });
|
|
3458
|
+
return runChunk(offset + CHUNK);
|
|
3459
|
+
}, () => runChunk(offset + CHUNK));
|
|
3460
|
+
};
|
|
3461
|
+
runChunk(0).then(
|
|
3462
|
+
(data) => {
|
|
3463
|
+
const items = {};
|
|
3464
|
+
for (const item of (data !== null && Array.isArray(data.items) ? data.items : [])) {
|
|
3465
|
+
if (item !== null && typeof item === "object" && typeof item.packageName === "string") items[item.packageName] = item;
|
|
3466
|
+
}
|
|
3467
|
+
const updatable = typeof data?.updatable === "number" ? data.updatable : 0;
|
|
3468
|
+
const unknown = typeof data?.unknown === "number" ? data.unknown : 0;
|
|
3469
|
+
const checked = typeof data?.checked === "number" ? data.checked : names.length;
|
|
3470
|
+
setBatchCheck({ status: "ready", at: Date.now(), checked, updatable, unknown, items });
|
|
3471
|
+
// 结论必须看得见:没有可更新项时**不动**"仅看可更新"筛选(否则列表突然变空)
|
|
3472
|
+
if (updatable === 0) setOnlyUpdatable(false);
|
|
3473
|
+
setMessage(
|
|
3474
|
+
(updatable > 0 ? t("checkAllUpdatesDone").replace("{n}", String(updatable)) : t("checkAllUpdateNone"))
|
|
3475
|
+
+ (unknown > 0 ? ";" + t("checkAllUpdateUnknownNote").replace("{n}", String(unknown)) : ""),
|
|
3476
|
+
);
|
|
3477
|
+
},
|
|
3478
|
+
(error) => {
|
|
3479
|
+
setBatchCheck({ status: "error", error: friendlyGithubError(error).message });
|
|
3480
|
+
setMessage(t("checkAllUpdateFailed") + ":" + friendlyGithubError(error).message);
|
|
3481
|
+
},
|
|
3482
|
+
);
|
|
3483
|
+
};
|
|
3219
3484
|
const toggle = (entry, enabled, confirmRisky = false) => {
|
|
3220
3485
|
setBusy(entry.entryId);
|
|
3221
3486
|
setMessage(null);
|
|
@@ -4275,14 +4540,297 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4275
4540
|
const label = primary?.name ?? t("repoLandSourceUnknown");
|
|
4276
4541
|
return t("repoLandTitle").replace("{source}", label);
|
|
4277
4542
|
};
|
|
4543
|
+
// issue #22 ①:批量检测结论查询(entry → 该项结论)。没查过 / 查中 / 出错一律 null =「这次没有结论」,
|
|
4544
|
+
// 于是卡片既不显示"可更新"也不显示"已最新"——不拿"没查过"冒充"已是最新"。
|
|
4545
|
+
const batchItemOf = (entry) => (batchCheck !== null && batchCheck.status === "ready"
|
|
4546
|
+
? batchCheck.items[String(entry.moduleName ?? "")] ?? null
|
|
4547
|
+
: null);
|
|
4548
|
+
|
|
4549
|
+
// ★ 0.5.93 改错(issue #22 ② 收口):徽章区**不再做任何宽度测量**。
|
|
4550
|
+
// 上一版用 ResizeObserver 读第一张卡片的徽章行宽度、再按估算宽度决定"显示几个 / 收成 +N"——
|
|
4551
|
+
// 测量值随卡片宽度抖动 ⇒ 徽章时不时多一个少一个(用户原话「徽章有挤压变化」)。
|
|
4552
|
+
// 现在:卡片上把徽章**全渲染**,靠 `.pc_badgeRow` 的 overflow:hidden + white-space:nowrap
|
|
4553
|
+
// 在纯 CSS 层截断(单行、固定高度、放不下的直接看不见,不给"还有 N 个"的提示);
|
|
4554
|
+
// 全部徽章在**详情页**一次看全。于是没有任何"测量 → 重算 → 重渲染"的反馈环。
|
|
4555
|
+
// ── issue #22 ②③:用户注解的读写与编辑块(备注 / 用户徽章 / 用户自建分组)───────────
|
|
4556
|
+
// 数据在**独立文件**里、走独立接口;这块出问题绝不影响插件列表(那是另一条通道)。
|
|
4557
|
+
const annotationKeyOf = (entry) => {
|
|
4558
|
+
const name = String(entry?.moduleName ?? "");
|
|
4559
|
+
return name !== "" ? name : String(entry?.rowId ?? "");
|
|
4560
|
+
};
|
|
4561
|
+
const annotationOf = (entry) => {
|
|
4562
|
+
const key = annotationKeyOf(entry);
|
|
4563
|
+
const record = key === "" ? null : annotations?.plugins?.[key];
|
|
4564
|
+
return record !== null && typeof record === "object" ? record : null;
|
|
4565
|
+
};
|
|
4566
|
+
const annotationsLoad = () => {
|
|
4567
|
+
call("/plugin-console/annotations").then(
|
|
4568
|
+
(data) => {
|
|
4569
|
+
setAnnotations(data !== null && typeof data === "object" ? data : null);
|
|
4570
|
+
// 手工改过文件 → 服务端规范化时丢弃了不合规内容,如实说(不静默)
|
|
4571
|
+
if (data !== null && Array.isArray(data.repaired) && data.repaired.length > 0) {
|
|
4572
|
+
setAnnotationsNote(t("annotationsRepaired").replace("{n}", String(data.repaired.length)));
|
|
4573
|
+
}
|
|
4574
|
+
},
|
|
4575
|
+
(error) => {
|
|
4576
|
+
setAnnotations(null);
|
|
4577
|
+
setAnnotationsNote(t("annotationsCorrupt").replace("{err}", friendlyGithubError(error).message));
|
|
4578
|
+
},
|
|
4579
|
+
);
|
|
4580
|
+
};
|
|
4581
|
+
/** 一次写动作:成功就用响应里的**整份最新状态**刷新(服务端保证回了全量),失败如实提示。 */
|
|
4582
|
+
const annotationWrite = (body, okText) => {
|
|
4583
|
+
const busyKey = String(body?.packageName ?? body?.id ?? body?.action ?? "");
|
|
4584
|
+
setAnnotationBusy(busyKey);
|
|
4585
|
+
return call("/plugin-console/annotations", body).then(
|
|
4586
|
+
(data) => {
|
|
4587
|
+
if (data !== null && data.plugins !== null && typeof data.plugins === "object") {
|
|
4588
|
+
setAnnotations({
|
|
4589
|
+
ok: true, file: data.file ?? null, exists: true, emptyFile: false, version: 1, updatedAt: 0,
|
|
4590
|
+
plugins: data.plugins, groups: Array.isArray(data.groups) ? data.groups : [],
|
|
4591
|
+
stats: data.stats ?? null, repaired: [], error: null,
|
|
4592
|
+
});
|
|
4593
|
+
}
|
|
4594
|
+
setAnnotationsNote(okText === undefined || okText === null ? null : okText);
|
|
4595
|
+
return data;
|
|
4596
|
+
},
|
|
4597
|
+
(error) => { setAnnotationsNote(t("annotationsFailed") + ":" + friendlyGithubError(error).message); return null; },
|
|
4598
|
+
).finally(() => setAnnotationBusy(null));
|
|
4599
|
+
};
|
|
4600
|
+
const userGroups = Array.isArray(annotations?.groups) ? annotations.groups : [];
|
|
4601
|
+
/** 某个包属于哪个用户分组(单值:取第一个命中的)。渲染请用 groupsOfPackage —— 分组允许重叠。 */
|
|
4602
|
+
const groupOfPackage = (moduleName) => {
|
|
4603
|
+
const name = String(moduleName ?? "");
|
|
4604
|
+
if (name === "") return null;
|
|
4605
|
+
for (const group of userGroups) {
|
|
4606
|
+
if (Array.isArray(group.packages) && group.packages.includes(name)) return group;
|
|
4607
|
+
}
|
|
4608
|
+
return null;
|
|
4609
|
+
};
|
|
4610
|
+
/**
|
|
4611
|
+
* 某个包属于**哪些**用户分组(分组允许重叠 ⇒ 可能多个)。
|
|
4612
|
+
* 与 groupOfPackage 的区别:那个用于"当前选中哪个组"(单值),这个用于渲染(多值)。
|
|
4613
|
+
*/
|
|
4614
|
+
const groupsOfPackage = (moduleName) => {
|
|
4615
|
+
const name = String(moduleName ?? "");
|
|
4616
|
+
if (name === "") return [];
|
|
4617
|
+
return userGroups.filter((group) => Array.isArray(group.packages) && group.packages.includes(name));
|
|
4618
|
+
};
|
|
4619
|
+
const createUserGroup = () => {
|
|
4620
|
+
const name = groupDraftName.trim();
|
|
4621
|
+
if (name === "") return;
|
|
4622
|
+
// ③:新建只走**回车/点按钮**这一条明确动作(绝不边打边建);成功后用服务端返回的
|
|
4623
|
+
// group.id 标一下"刚建好"(只为了在主列表的横框上播一次入场动画,数据仍是服务端那份)。
|
|
4624
|
+
setGroupDraftName("");
|
|
4625
|
+
annotationWrite({ action: "group-create", name }, t("groupCreated").replace("{name}", name)).then((data) => {
|
|
4626
|
+
const id = data !== null && data !== undefined && data.group !== null && data.group !== undefined
|
|
4627
|
+
? String(data.group.id ?? "")
|
|
4628
|
+
: "";
|
|
4629
|
+
if (id === "") return;
|
|
4630
|
+
setGroupJustCreated(id);
|
|
4631
|
+
try {
|
|
4632
|
+
// 动画只有 220ms;1.2s 后把这个标记撤掉,避免以后重渲染时又播一次
|
|
4633
|
+
window.setTimeout(() => setGroupJustCreated((prev) => (prev === id ? null : prev)), 1200);
|
|
4634
|
+
} catch {}
|
|
4635
|
+
});
|
|
4636
|
+
};
|
|
4637
|
+
/**
|
|
4638
|
+
* 打开「备注插件」面板(用户定稿:面板打开时插件列表**收起**,只留一行摘要)。
|
|
4639
|
+
* 每次打开都回到收起 + 空查询 —— 不给"上次展开到一半"的残留状态。
|
|
4640
|
+
*/
|
|
4641
|
+
const openAnnotationsPanel = () => {
|
|
4642
|
+
setAnnotationsListOpen(false);
|
|
4643
|
+
setAnnotationsQuery("");
|
|
4644
|
+
setAnnotationsPanelOpen(true);
|
|
4645
|
+
annotationsLoad();
|
|
4646
|
+
};
|
|
4647
|
+
/** 工具栏那个按钮是**开关**:开着再点就关掉("点了没反应"算缺陷)。 */
|
|
4648
|
+
const toggleAnnotationsPanel = () => {
|
|
4649
|
+
if (annotationsPanelOpen) { setAnnotationsPanelOpen(false); return; }
|
|
4650
|
+
openAnnotationsPanel();
|
|
4651
|
+
};
|
|
4652
|
+
// issue #22 ②:注解面板里的插件行 —— 默认收起;展开后按 包名 / rowId / 已有备注内容 即时过滤。
|
|
4653
|
+
// 查询词只读不写 ⇒ 边打边筛的过程中用户正在输入的内容**不会被清掉**。
|
|
4654
|
+
const annotationListEntries = state.status === "ready" ? state.data.entries : [];
|
|
4655
|
+
const annotationsQueryNorm = annotationsQuery.trim().toLowerCase();
|
|
4656
|
+
const annotationListFiltered = annotationsQueryNorm === ""
|
|
4657
|
+
? annotationListEntries
|
|
4658
|
+
: annotationListEntries.filter((entry) => [entry.moduleName, entry.rowId, annotationOf(entry)?.note]
|
|
4659
|
+
.some((value) => String(value ?? "").toLowerCase().includes(annotationsQueryNorm)));
|
|
4660
|
+
/**
|
|
4661
|
+
* 用户注解编辑块 —— 详情页与「备注插件」面板**共用同一份**(两处交互必须一模一样)。
|
|
4662
|
+
* · 备注:点一下就地编辑,回车保存、Esc 取消;存空 = 清空(清空后**不占位**)。
|
|
4663
|
+
* · 徽章:∪ 追加(系统徽章在卡片上是另一组,**绝不替换、绝不合并**);× 删除后可撤销一次。
|
|
4664
|
+
* · 没有备注也没有徽章时:只显示一个「+ 加备注」入口(用户定稿的那一种,不显示空壳)。
|
|
4665
|
+
*/
|
|
4666
|
+
const annotationBlock = (entry) => {
|
|
4667
|
+
const key = annotationKeyOf(entry);
|
|
4668
|
+
const packageName = String(entry?.moduleName ?? key);
|
|
4669
|
+
const record = annotationOf(entry);
|
|
4670
|
+
const note = typeof record?.note === "string" ? record.note : "";
|
|
4671
|
+
const badges = Array.isArray(record?.badges) ? record.badges : [];
|
|
4672
|
+
const group = groupOfPackage(packageName);
|
|
4673
|
+
const editing = noteEdit !== null && noteEdit.key === key;
|
|
4674
|
+
const busy = annotationBusy === key || annotationBusy === packageName;
|
|
4675
|
+
const draft = badgeDraft[key] ?? "";
|
|
4676
|
+
const undo = badgeUndo[key];
|
|
4677
|
+
const writeNote = (text) => annotationWrite({ action: "note", packageName, rowId: entry?.rowId ?? null, note: text }, text.trim() === "" ? t("noteCleared") : t("noteSaved"));
|
|
4678
|
+
const writeBadges = (list, okText) => annotationWrite({ action: "badges", packageName, rowId: entry?.rowId ?? null, badges: list }, okText);
|
|
4679
|
+
const addBadge = () => {
|
|
4680
|
+
const text = draft.trim();
|
|
4681
|
+
if (text === "") return;
|
|
4682
|
+
setBadgeDraft((prev) => ({ ...prev, [key]: "" }));
|
|
4683
|
+
if (badges.includes(text)) return;
|
|
4684
|
+
if (badges.length >= 12) { setAnnotationsNote(t("badgeMax").replace("{n}", "12")); return; }
|
|
4685
|
+
writeBadges([...badges, text], t("badgeAdded").replace("{b}", text));
|
|
4686
|
+
};
|
|
4687
|
+
return el("div", { className: styles.userNote, "data-annot": "true" },
|
|
4688
|
+
editing
|
|
4689
|
+
? el("div", null,
|
|
4690
|
+
el("textarea", {
|
|
4691
|
+
className: styles.noteInput,
|
|
4692
|
+
rows: 2,
|
|
4693
|
+
value: noteEdit.draft,
|
|
4694
|
+
placeholder: t("notePlaceholder"),
|
|
4695
|
+
"aria-label": t("noteLabel"),
|
|
4696
|
+
onChange: (event) => setNoteEdit({ key, draft: String(event.target.value ?? "") }),
|
|
4697
|
+
onKeyDown: (event) => {
|
|
4698
|
+
if (event.key === "Escape") { event.preventDefault(); setNoteEdit(null); return; }
|
|
4699
|
+
if (event.key === "Enter" && event.shiftKey !== true) {
|
|
4700
|
+
event.preventDefault();
|
|
4701
|
+
const text = noteEdit.draft;
|
|
4702
|
+
setNoteEdit(null);
|
|
4703
|
+
writeNote(text);
|
|
4704
|
+
}
|
|
4705
|
+
},
|
|
4706
|
+
}),
|
|
4707
|
+
el("p", { className: styles.note, style: { margin: 0 } }, t("noteSaveHint")))
|
|
4708
|
+
: el("div", { className: styles.annotRow },
|
|
4709
|
+
note !== "" ? el("p", { className: styles.desc, style: { margin: 0, whiteSpace: "pre-wrap", flex: "1 1 200px" } }, note) : null,
|
|
4710
|
+
el("button", {
|
|
4711
|
+
type: "button",
|
|
4712
|
+
className: styles.toggle,
|
|
4713
|
+
disabled: busy,
|
|
4714
|
+
title: t("notePlaceholder"),
|
|
4715
|
+
onClick: () => setNoteEdit({ key, draft: note }),
|
|
4716
|
+
}, note !== "" ? t("noteEdit") : t("noteEmpty"))),
|
|
4717
|
+
el("div", { className: styles.annotRow },
|
|
4718
|
+
el("span", { className: styles.status }, t("badgeLabel") + ":"),
|
|
4719
|
+
badges.map((badge) => el("span", { key: "ub-" + badge, className: styles.tag, "data-user-badge": "true" },
|
|
4720
|
+
badge,
|
|
4721
|
+
el("button", {
|
|
4722
|
+
type: "button",
|
|
4723
|
+
className: styles.tag,
|
|
4724
|
+
"data-more": "true",
|
|
4725
|
+
title: t("badgeRemove"),
|
|
4726
|
+
"aria-label": t("badgeRemove"),
|
|
4727
|
+
disabled: busy,
|
|
4728
|
+
onClick: () => {
|
|
4729
|
+
setBadgeUndo((prev) => ({ ...prev, [key]: badges }));
|
|
4730
|
+
writeBadges(badges.filter((item) => item !== badge), t("badgeRemoved").replace("{b}", badge));
|
|
4731
|
+
},
|
|
4732
|
+
}, "×"))),
|
|
4733
|
+
el("input", {
|
|
4734
|
+
type: "text",
|
|
4735
|
+
value: draft,
|
|
4736
|
+
placeholder: t("badgeAddPlaceholder"),
|
|
4737
|
+
"aria-label": t("badgeAddPlaceholder"),
|
|
4738
|
+
disabled: busy,
|
|
4739
|
+
style: { width: 130, font: "inherit", padding: "2px 6px" },
|
|
4740
|
+
onChange: (event) => setBadgeDraft((prev) => ({ ...prev, [key]: String(event.target.value ?? "") })),
|
|
4741
|
+
onKeyDown: (event) => { if (event.key === "Enter") { event.preventDefault(); addBadge(); } },
|
|
4742
|
+
}),
|
|
4743
|
+
undo !== undefined
|
|
4744
|
+
? el("button", {
|
|
4745
|
+
type: "button",
|
|
4746
|
+
className: styles.toggle,
|
|
4747
|
+
disabled: busy,
|
|
4748
|
+
onClick: () => {
|
|
4749
|
+
writeBadges(undo, t("badgeUndoDone"));
|
|
4750
|
+
setBadgeUndo((prev) => { const next = { ...prev }; delete next[key]; return next; });
|
|
4751
|
+
},
|
|
4752
|
+
}, t("badgeUndo"))
|
|
4753
|
+
: null),
|
|
4754
|
+
el("div", { className: styles.annotRow },
|
|
4755
|
+
el("span", { className: styles.status }, t("groupLabel") + ":"),
|
|
4756
|
+
// #5:就在这里新建分组,并把当前插件直接拉进去(回车创建)
|
|
4757
|
+
detailGroupDraft === null || detailGroupDraft.key !== key
|
|
4758
|
+
? el("button", {
|
|
4759
|
+
type: "button",
|
|
4760
|
+
className: styles.toggle,
|
|
4761
|
+
"data-newgroup": "true",
|
|
4762
|
+
title: t("groupNewInlineHint"),
|
|
4763
|
+
disabled: busy,
|
|
4764
|
+
onClick: () => setDetailGroupDraft({ key, draft: "" }),
|
|
4765
|
+
}, "+ " + t("groupNewInline"))
|
|
4766
|
+
: el("input", {
|
|
4767
|
+
type: "text",
|
|
4768
|
+
autoFocus: true,
|
|
4769
|
+
value: detailGroupDraft.draft,
|
|
4770
|
+
placeholder: t("groupNewInlinePlaceholder"),
|
|
4771
|
+
"aria-label": t("groupNewInline"),
|
|
4772
|
+
style: { font: "inherit", padding: "2px 6px", width: 160 },
|
|
4773
|
+
onChange: (event) => setDetailGroupDraft({ key, draft: String(event.target.value ?? "") }),
|
|
4774
|
+
onKeyDown: (event) => {
|
|
4775
|
+
if (event.key === "Escape") { event.preventDefault(); setDetailGroupDraft(null); return; }
|
|
4776
|
+
if (event.key !== "Enter") return;
|
|
4777
|
+
event.preventDefault();
|
|
4778
|
+
const name = String(detailGroupDraft.draft ?? "").trim();
|
|
4779
|
+
if (name === "") { setDetailGroupDraft(null); return; }
|
|
4780
|
+
setDetailGroupDraft(null);
|
|
4781
|
+
// 建组 → 用返回的全量 groups 找到它 → 把当前插件拉进去
|
|
4782
|
+
annotationWrite({ action: "group-create", name }, t("groupCreated").replace("{name}", name))
|
|
4783
|
+
.then((data) => {
|
|
4784
|
+
if (data === null) return;
|
|
4785
|
+
const created = (Array.isArray(data.groups) ? data.groups : [])
|
|
4786
|
+
.find((item) => String(item.name) === name);
|
|
4787
|
+
if (created !== undefined && created !== null) {
|
|
4788
|
+
annotationWrite({ action: "group-assign", id: created.id, packageName }, t("groupAssigned").replace("{name}", name));
|
|
4789
|
+
}
|
|
4790
|
+
});
|
|
4791
|
+
},
|
|
4792
|
+
}),
|
|
4793
|
+
el("select", {
|
|
4794
|
+
value: group?.id ?? "",
|
|
4795
|
+
"aria-label": t("groupLabel"),
|
|
4796
|
+
disabled: busy,
|
|
4797
|
+
style: { font: "inherit", padding: "2px 6px" },
|
|
4798
|
+
onChange: (event) => {
|
|
4799
|
+
const picked = String(event.target.value ?? "");
|
|
4800
|
+
if (picked === "") {
|
|
4801
|
+
if (group === null) return;
|
|
4802
|
+
annotationWrite({ action: "group-assign", id: group.id, packageName, member: false }, t("groupUnassigned"));
|
|
4803
|
+
return;
|
|
4804
|
+
}
|
|
4805
|
+
const pickedName = String(userGroups.find((item) => item.id === picked)?.name ?? "");
|
|
4806
|
+
annotationWrite({ action: "group-assign", id: picked, packageName }, t("groupAssigned").replace("{name}", pickedName));
|
|
4807
|
+
},
|
|
4808
|
+
},
|
|
4809
|
+
el("option", { value: "" }, t("groupNone")),
|
|
4810
|
+
userGroups.map((item) => el("option", { key: "gs-" + item.id, value: item.id }, String(item.name))))));
|
|
4811
|
+
};
|
|
4278
4812
|
const extraIds = new Set((state.status === "ready" ? state.data.patch?.inserts ?? [] : []));
|
|
4279
4813
|
const extraCount = state.status === "ready" ? state.data.entries.filter((entry) => entry.extra === true || extraIds.has(entry.rowId)).length : 0;
|
|
4814
|
+
// issue #22 ②③:打开面板即读一次注解(读失败只提示,插件列表照常)
|
|
4815
|
+
react.useEffect(() => { annotationsLoad(); }, []);
|
|
4280
4816
|
const pkgRootOf = (m) => { const mm = String(m ?? ""); const x = mm.match(/^(@[^/]+\/[^/]+)(?:\/.*)?$/u); return x ? x[1] : mm; };
|
|
4281
4817
|
const filteredEntries = state.status === "ready"
|
|
4282
4818
|
? state.data.entries.filter((entry) => {
|
|
4283
4819
|
if (extraOnly && !(entry.extra === true || extraIds.has(entry.rowId))) return false;
|
|
4820
|
+
// issue #22 ①:「仅看可更新」只筛**本次批量检测确实查到新版**的行。
|
|
4821
|
+
// 没查到结论的行(未检测到 / 没查过)**不算**可更新 —— 与"没查到 ≠ 已最新"同一条纪律。
|
|
4822
|
+
if (onlyUpdatable && batchItemOf(entry)?.status !== "update") return false;
|
|
4284
4823
|
if (installedQueryNorm === "") return true;
|
|
4285
|
-
|
|
4824
|
+
// ★ 用户 2026-10-27:搜索也要命中**备注**(备注是可用来命名的,只按包名搜会漏)
|
|
4825
|
+
const noteText = (() => {
|
|
4826
|
+
const key = annotationKeyOf(entry);
|
|
4827
|
+
if (key === "") return "";
|
|
4828
|
+
const rec = annotations !== null && annotations.plugins !== null && annotations.plugins !== undefined
|
|
4829
|
+
? annotations.plugins[key] ?? null
|
|
4830
|
+
: null;
|
|
4831
|
+
return typeof rec?.note === "string" ? rec.note : "";
|
|
4832
|
+
})();
|
|
4833
|
+
return [entry.moduleName, entry.entryId, entry.rowId, noteText]
|
|
4286
4834
|
.some((value) => String(value ?? "").toLowerCase().includes(installedQueryNorm));
|
|
4287
4835
|
})
|
|
4288
4836
|
: [];
|
|
@@ -4302,10 +4850,33 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4302
4850
|
familyCounts.set(root, set);
|
|
4303
4851
|
}
|
|
4304
4852
|
const familySize = (root) => (familyCounts.get(root)?.size ?? 0);
|
|
4305
|
-
const familyCards = filteredEntries.map((entry) => {
|
|
4853
|
+
const familyCards = filteredEntries.map((entry, cardIndex) => {
|
|
4306
4854
|
const familyRoot = String(entry.moduleName ?? "").startsWith("@deepseek-ai/") ? null : pkgRootOf(entry.moduleName);
|
|
4307
4855
|
void familyRoot;
|
|
4308
4856
|
const open = details !== null && details.entryId === entry.entryId;
|
|
4857
|
+
// issue #22 ①:这张卡本次的批量检测结论(null = 没查过/查中/查失败 ⇒ 不加任何"已最新"标签)
|
|
4858
|
+
const batchItem = batchItemOf(entry);
|
|
4859
|
+
// issue #22 ②:卡片徽章 = **系统徽章(现有顺序,一条不少)** + 用户徽章(追加在最后)。
|
|
4860
|
+
// 系统徽章用现有实心/描边样式(props 原样搬过来),用户徽章带 data-user-badge(虚线+浅底)。
|
|
4861
|
+
const systemBadges = [
|
|
4862
|
+
{ key: "enabled", label: t(entry.enabled ? "enabledTag" : "disabledTag"), props: { "data-enabled": entry.enabled ? "true" : "false" } },
|
|
4863
|
+
entry.extra === true ? { key: "extra", label: t("extraTag"), props: { "data-user": "true" } } : null,
|
|
4864
|
+
entry.protected ? { key: "protected", label: t("protectedTag"), props: { "data-user": "true" } } : null,
|
|
4865
|
+
entry.userDisabled ? { key: "userDisabled", label: t("userDisabledTag"), props: { "data-user": "true" } } : null,
|
|
4866
|
+
entry.userForced ? { key: "userForced", label: t("userForcedTag"), props: { "data-user": "true" } } : null,
|
|
4867
|
+
batchItem?.status === "update"
|
|
4868
|
+
? { key: "batch-update", label: t("batchUpdateAvailable").replace("{v}", String(batchItem.target ?? "?")), props: { "data-pending": "true", title: t("batchUpdateAvailableTip") } }
|
|
4869
|
+
: batchItem?.status === "current"
|
|
4870
|
+
? { key: "batch-current", label: t("batchUpToDate"), props: { "data-enabled": "true" } }
|
|
4871
|
+
: batchItem?.status === "unknown"
|
|
4872
|
+
? { key: "batch-unknown", label: t("batchUpdateUnknown"), props: { title: t("batchUpdateUnknownTip") + (batchItem.error ? "(" + String(batchItem.error) + ")" : "") } }
|
|
4873
|
+
: null,
|
|
4874
|
+
entry.disableSource === "bundle" ? { key: "bundle", label: t("disabledByBundleTag"), props: { "data-user": "true", "data-source": "bundle" } } : null,
|
|
4875
|
+
entry.forcedNotLoaded === true ? { key: "ineffective", label: t("forcedNotLoadedTag"), props: { "data-user": "true", "data-source": "ineffective" } } : null,
|
|
4876
|
+
].filter((badge) => badge !== null);
|
|
4877
|
+
const userBadges = (Array.isArray(annotationOf(entry)?.badges) ? annotationOf(entry).badges : [])
|
|
4878
|
+
.map((label) => ({ key: "user-" + label, label: String(label), props: { "data-user-badge": "true", title: t("badgeLabel") } }));
|
|
4879
|
+
const allBadges = [...systemBadges, ...userBadges];
|
|
4309
4880
|
let detailPanel = null;
|
|
4310
4881
|
if (open) {
|
|
4311
4882
|
if (details.status === "loading") {
|
|
@@ -4320,20 +4891,10 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4320
4891
|
const compatInfo = state.status === "ready" && state.data.compatPending
|
|
4321
4892
|
? (state.data.compatPending.pending ?? []).find((p) => p.rowId === entry.rowId) ?? null
|
|
4322
4893
|
: null;
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
entry.adoptable
|
|
4328
|
-
? (entry.adoptable.basis === "verdict-rescinded"
|
|
4329
|
-
// 0.5.34:旧判据的误报已被当前判据撤回(真机 dshmarket 现场)——如实说是"判据更新",
|
|
4330
|
-
// 不能说成"插件已适配"(那会让人以为作者发过新版)
|
|
4331
|
-
? t("adoptableRescinded").replace("{version}", String(entry.adoptable.version ?? "?"))
|
|
4332
|
-
: t("adoptableDetected").replace("{version}", String(entry.adoptable.version ?? "?")).replace("{prev}", String(compatInfo?.version ?? "?")))
|
|
4333
|
-
: t("pendingCompatHint") + (compatInfo && compatInfo.checkNote ? ";已知校验:" + compatInfo.checkNote : ""))
|
|
4334
|
-
: null,
|
|
4335
|
-
meta !== null && meta.version ? el("div", { className: styles.rowTop },
|
|
4336
|
-
el("p", { className: styles.status }, t("versionLabel") + ":" + meta.version),
|
|
4894
|
+
// ★ issue #22 ②(用户定稿):动作按钮排在详情页**最后**(原来挂在"版本"那一行,夹在中间)。
|
|
4895
|
+
// 判据/禁用条件/文案逐字搬过来,一个字都没改,只换了位置。
|
|
4896
|
+
const detailActionRow = meta !== null && meta.version
|
|
4897
|
+
? el("div", { className: styles.rowTop },
|
|
4337
4898
|
el("button", {
|
|
4338
4899
|
type: "button",
|
|
4339
4900
|
className: styles.toggle,
|
|
@@ -4365,7 +4926,29 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4365
4926
|
);
|
|
4366
4927
|
},
|
|
4367
4928
|
}, adaptUnlocking === entry.entryId ? t("loadingPhase") : t("adaptUnlockBtn"))
|
|
4368
|
-
: null)
|
|
4929
|
+
: null)
|
|
4930
|
+
: null;
|
|
4931
|
+
detailPanel = el("div", { className: styles.detail },
|
|
4932
|
+
// ★ 0.5.93(issue #22 ② 收口):卡片上放不下的徽章**不提示、不展开**,
|
|
4933
|
+
// 全部徽章(系统徽章 + 用户徽章,顺序与卡片一致)在详情页里一次看全 ——
|
|
4934
|
+
// 这也是"卡片不再出现 +N"之后"看不到的徽章去哪了"的唯一答案。
|
|
4935
|
+
el("div", { className: styles.badgeAll, "data-allbadges": "true", title: t("badgesAll").replace("{n}", String(allBadges.length)) },
|
|
4936
|
+
allBadges.map((badge) => el("span", { key: "detail-" + badge.key, className: styles.tag, ...badge.props }, badge.label))),
|
|
4937
|
+
// ★ issue #22 ②(用户定稿的详情页顺序):用户备注/用户徽章放**最前面**,
|
|
4938
|
+
// 系统诊断 → 版本/仓库/说明摘要 → 动作按钮依次在其后。
|
|
4939
|
+
annotationBlock(entry),
|
|
4940
|
+
meta !== null ? el("p", { className: styles.desc }, meta.description ?? t("noDetail")) : null,
|
|
4941
|
+
entry.pendingCompat === true
|
|
4942
|
+
? el("p", { className: styles.status, "data-pending": entry.adoptable ? "false" : "true" },
|
|
4943
|
+
entry.adoptable
|
|
4944
|
+
? (entry.adoptable.basis === "verdict-rescinded"
|
|
4945
|
+
// 0.5.34:旧判据的误报已被当前判据撤回(真机 dshmarket 现场)——如实说是"判据更新",
|
|
4946
|
+
// 不能说成"插件已适配"(那会让人以为作者发过新版)
|
|
4947
|
+
? t("adoptableRescinded").replace("{version}", String(entry.adoptable.version ?? "?"))
|
|
4948
|
+
: t("adoptableDetected").replace("{version}", String(entry.adoptable.version ?? "?")).replace("{prev}", String(compatInfo?.version ?? "?")))
|
|
4949
|
+
: t("pendingCompatHint") + (compatInfo && compatInfo.checkNote ? ";已知校验:" + compatInfo.checkNote : ""))
|
|
4950
|
+
: null,
|
|
4951
|
+
meta !== null && meta.version ? el("p", { className: styles.status }, t("versionLabel") + ":" + meta.version) : null,
|
|
4369
4952
|
updateCheck !== null && updateCheck.status === "ready" && updateCheck.migrate && updateCheck.migrate.compatible
|
|
4370
4953
|
? el("div", null,
|
|
4371
4954
|
el("div", { className: styles.rowTop },
|
|
@@ -4418,28 +5001,34 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4418
5001
|
readme.summary ? el("p", { className: styles.desc }, readme.summary) : null,
|
|
4419
5002
|
)
|
|
4420
5003
|
: meta === null ? el("p", { className: styles.status }, t("noDetail")) : null,
|
|
5004
|
+
detailActionRow,
|
|
4421
5005
|
);
|
|
4422
5006
|
}
|
|
4423
5007
|
}
|
|
4424
5008
|
return el("li", { key: entry.entryId, className: styles.row },
|
|
4425
|
-
el("div", { className: styles.rowTop },
|
|
4426
|
-
el("strong", { className: styles.name, title: entry.moduleName }, (() => {
|
|
5009
|
+
el("div", { className: styles.rowTop, style: { minWidth: 0 } },
|
|
5010
|
+
el("strong", { className: styles.name, title: entry.moduleName, style: { minWidth: 0 } }, (() => {
|
|
4427
5011
|
const famRoot = famRootOf(entry);
|
|
4428
5012
|
const mn = String(entry.moduleName ?? "");
|
|
4429
|
-
|
|
5013
|
+
const shortName = famRoot !== null && familySize(famRoot) > 1 && mn.startsWith(famRoot + "/")
|
|
4430
5014
|
? mn.slice(famRoot.length + 1)
|
|
4431
5015
|
: moduleShortName(entry.moduleName);
|
|
5016
|
+
// ★ 用户 2026-10-26:备注可以**覆盖显示名**。有备注时标题用备注(强调色 + ✎ 标记),
|
|
5017
|
+
// 原短名降到旁边的小字 —— 一眼能看出"这个插件被我备注过",与没备注的区分开。
|
|
5018
|
+
const rec = annotationOf(entry);
|
|
5019
|
+
const custom = typeof rec?.note === "string" ? rec.note.trim() : "";
|
|
5020
|
+
if (custom === "") return shortName;
|
|
5021
|
+
return el("span", { "data-note-name": "true", style: { display: "inline-flex", alignItems: "baseline", gap: 6, minWidth: 0 } },
|
|
5022
|
+
el("span", { className: styles.noteName ?? undefined, style: { color: "var(--dsw-alias-state-business-primary)", fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, custom),
|
|
5023
|
+
el("span", { style: { opacity: 0.55, fontWeight: 400, fontSize: ".85em", whiteSpace: "nowrap" } }, shortName));
|
|
4432
5024
|
})()),
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
entry.disableSource === "bundle" ? el("span", { className: styles.tag, "data-user": "true", "data-source": "bundle" }, t("disabledByBundleTag")) : null,
|
|
4441
|
-
// ★ 0.5.76:补丁写了强制开启、运行时却仍没起来 ⇒ 这条覆盖是**无效的**,必须说出来
|
|
4442
|
-
entry.forcedNotLoaded === true ? el("span", { className: styles.tag, "data-user": "true", "data-source": "ineffective" }, t("forcedNotLoadedTag")) : null,
|
|
5025
|
+
// ★ 0.5.93(issue #22 ② 收口):徽章区**单行不换行**,放不下的直接看不见
|
|
5026
|
+
// (纯 CSS:`.pc_badgeRow` 固定高度 + overflow:hidden + white-space:nowrap),
|
|
5027
|
+
// 卡片上**不给** "+N"、不给"还有几个"的任何提示,也不做宽度测量 ——
|
|
5028
|
+
// 行为与这个功能上线前一致;全部徽章去详情页看。
|
|
5029
|
+
// 顺序(用户定稿):**先系统徽章(现有顺序一个字没改)→ 再用户徽章(描边样式)**。
|
|
5030
|
+
el("div", { className: styles.badgeRow },
|
|
5031
|
+
allBadges.map((badge) => el("span", { key: badge.key, className: styles.tag, ...badge.props }, badge.label))),
|
|
4443
5032
|
),
|
|
4444
5033
|
el("div", { className: styles.meta },
|
|
4445
5034
|
el("span", { className: styles.phase, "data-phase": entry.fiberPhase ?? "unobserved" },
|
|
@@ -4475,6 +5064,20 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4475
5064
|
title: entry.pendingCompat === true ? (entry.compatRescinded === true ? t("compatRescindedHint") : t("pendingCompatHint")) : undefined,
|
|
4476
5065
|
onClick: () => toggle(entry, !entry.enabled),
|
|
4477
5066
|
}, busy === entry.entryId ? t("loadingPhase") : (entry.pendingCompat === true ? (entry.compatRescinded === true ? t("compatRescindedTag") : t("pendingCompatTag")) : t(entry.enabled ? "off" : "on"))),
|
|
5067
|
+
// ★ issue #22 ①:批量检测到新版的卡片直接给「更新」按钮 —— 与详情卡片里那个按钮
|
|
5068
|
+
// **完全同一条路**(startJob(-, moduleName, github, plugin, update=true, -, target)),
|
|
5069
|
+
// 目标是服务端唯一判据算出的 target;点下去走的是既有安装/更新作业,不新增任何写盘路径。
|
|
5070
|
+
batchItem !== null && batchItem.status === "update"
|
|
5071
|
+
? el("button", {
|
|
5072
|
+
type: "button",
|
|
5073
|
+
className: styles.toggle,
|
|
5074
|
+
title: t("batchUpdateAvailableTip"),
|
|
5075
|
+
disabled: installing === entry.moduleName || inFlight.has(entry.moduleName),
|
|
5076
|
+
onClick: () => startJob("", entry.moduleName, "github", "plugin", true, null, batchItem.target ?? null),
|
|
5077
|
+
}, installing === entry.moduleName
|
|
5078
|
+
? t("installing")
|
|
5079
|
+
: (entry.pendingCompat === true ? t("updateAndAdapt") : t("update") + " → v" + String(batchItem.target ?? "?")))
|
|
5080
|
+
: null,
|
|
4478
5081
|
// ★ 0.5.76:补丁写了强制开启但运行时没起来 ⇒ 给一个撤销覆盖的入口
|
|
4479
5082
|
entry.userForced === true ? el("button", {
|
|
4480
5083
|
type: "button",
|
|
@@ -4602,11 +5205,29 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4602
5205
|
// 组头放到最底部:普通条目在前,全家桶(组头+子包卡片)整体在后
|
|
4603
5206
|
const nonFamilyCards = [];
|
|
4604
5207
|
const familySections = new Map();
|
|
5208
|
+
// issue #22 ③:用户自建分组 → 折叠区块。**官方 family 优先**(用户拍定的规则):
|
|
5209
|
+
// 属于官方 family 的插件即使被归进用户分组,也**留在官方 family 区块里**,不进用户分组区块 ——
|
|
5210
|
+
// 官方 family 的渲染因此一个字都没变,两者共存且不会出现同一插件两处重复。
|
|
5211
|
+
const userGroupBuckets = new Map();
|
|
4605
5212
|
familyCards.forEach((card, i) => {
|
|
4606
5213
|
const e = filteredEntries[i];
|
|
4607
5214
|
if (e === undefined) { nonFamilyCards.push(card); return; }
|
|
4608
5215
|
const root = famRootOf(e);
|
|
4609
|
-
|
|
5216
|
+
// ★ 用户 2026-10-26:**用户分组优先** —— 被归进自己分组的插件,卡片直接住进那个分组区块
|
|
5217
|
+
// (像全家桶一样能展开看到卡片),官方 family 区块里不再重复渲染它。
|
|
5218
|
+
// ★ 分组允许重叠:一个插件可以同时出现在多个分组区块里(用户 2026-10-27 定稿的规则)
|
|
5219
|
+
const userGroupsOf = groupsOfPackage(e.moduleName);
|
|
5220
|
+
if (userGroupsOf.length > 0) {
|
|
5221
|
+
for (const g of userGroupsOf) {
|
|
5222
|
+
if (!userGroupBuckets.has(g.id)) userGroupBuckets.set(g.id, []);
|
|
5223
|
+
userGroupBuckets.get(g.id).push(card);
|
|
5224
|
+
}
|
|
5225
|
+
return;
|
|
5226
|
+
}
|
|
5227
|
+
if (root === null || familySize(root) <= 1) {
|
|
5228
|
+
nonFamilyCards.push(card);
|
|
5229
|
+
return;
|
|
5230
|
+
}
|
|
4610
5231
|
if (!familySections.has(root)) familySections.set(root, { head: familyHeaders.find((h) => h && h.key === "family-" + root) ?? null, cards: [] });
|
|
4611
5232
|
familySections.get(root).cards.push(card);
|
|
4612
5233
|
});
|
|
@@ -4671,7 +5292,131 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4671
5292
|
),
|
|
4672
5293
|
),
|
|
4673
5294
|
));
|
|
4674
|
-
|
|
5295
|
+
// issue #22 ③(0.5.93 收口,用户定稿):用户自建分组在主列表里是**横框**,外观与官方 family
|
|
5296
|
+
// 区块**同一套视觉语言**(标题行 + 折叠箭头 + 容器 + 成员数徽章)。三条行为:
|
|
5297
|
+
// · 空分组(刚建出来、还没放插件)**也渲染** —— 否则"新建分组"在列表里根本看不到,
|
|
5298
|
+
// 等于点了没反应;
|
|
5299
|
+
// · 框里可以直接**加入 / 移出**插件(走 group-assign;服务端保证一个包只属于一个分组,
|
|
5300
|
+
// 移出只解除归属,不删插件、不动备注与徽章);
|
|
5301
|
+
// · **只含官方 family 成员**的分组仍不画(官方 family 优先:那些成员留在 family 区块里,
|
|
5302
|
+
// 绝不出现同一插件两处重复);
|
|
5303
|
+
// · 刚建出来的那个横框多挂一个 pc_groupNew(220ms 入场动画;prefers-reduced-motion 下不播)。
|
|
5304
|
+
// 数据来源只有服务端那一份 groups(annotationWrite 用响应里的全量状态覆盖 annotations),
|
|
5305
|
+
// 客户端额外只记一个"刚建好的 id"用于播动画,不维护第二份状态。
|
|
5306
|
+
const userGroupFrame = (group, bucket) => {
|
|
5307
|
+
const members = (Array.isArray(group.packages) ? group.packages : []).map((item) => String(item));
|
|
5308
|
+
const inGroup = new Set(members);
|
|
5309
|
+
const candidates = [];
|
|
5310
|
+
for (const entry of (state.status === "ready" ? state.data.entries : [])) {
|
|
5311
|
+
const name = String(entry.moduleName ?? "");
|
|
5312
|
+
if (name === "" || inGroup.has(name) || candidates.includes(name)) continue;
|
|
5313
|
+
candidates.push(name);
|
|
5314
|
+
}
|
|
5315
|
+
const fresh = groupJustCreated !== null && String(group.id) === String(groupJustCreated);
|
|
5316
|
+
const header = el("div", { className: styles.rowTop, style: { alignItems: "center" } },
|
|
5317
|
+
el("strong", { className: styles.name, style: { flex: "0 0 auto", whiteSpace: "nowrap" }, title: String(group.name) }, String(group.name)),
|
|
5318
|
+
el("span", { className: styles.tag, "data-user-badge": "true" }, t("userGroupTag") + " · " + members.length + " " + t("familySubs")),
|
|
5319
|
+
el("button", {
|
|
5320
|
+
type: "button",
|
|
5321
|
+
className: styles.toggle,
|
|
5322
|
+
style: { whiteSpace: "nowrap", flexShrink: 0 },
|
|
5323
|
+
disabled: annotationBusy === group.id,
|
|
5324
|
+
onClick: () => annotationWrite({ action: "group-collapse", id: group.id, collapsed: group.collapsed !== true }, null),
|
|
5325
|
+
}, group.collapsed === true ? "▸ " + t("familyExpand") : "▾ " + t("familyCollapse")),
|
|
5326
|
+
// ★ 用户 2026-10-27:分组框也要有「批量检测更新 / 一键启用已适配 / 删除分组」
|
|
5327
|
+
el("button", {
|
|
5328
|
+
type: "button",
|
|
5329
|
+
className: styles.toggle,
|
|
5330
|
+
style: { whiteSpace: "nowrap", flexShrink: 0 },
|
|
5331
|
+
disabled: annotationBusy === group.id,
|
|
5332
|
+
onClick: () => {
|
|
5333
|
+
// 批检:组内每个成员各查一次(单包接口复用,判据不分叉)
|
|
5334
|
+
setMessage(t("checkingUpdate"));
|
|
5335
|
+
const names = members.slice();
|
|
5336
|
+
Promise.all(names.map((nm) => call("/plugin-console/check-update", { packageName: nm }).catch(() => null)))
|
|
5337
|
+
.then((list) => {
|
|
5338
|
+
const hits = list.filter((d) => d !== null && typeof d?.target === "string" && d.target !== "")
|
|
5339
|
+
setMessage(hits.length > 0 ? t("familyUpdate") + ":" + hits.length + " / " + names.length : t("upToDate"));
|
|
5340
|
+
});
|
|
5341
|
+
},
|
|
5342
|
+
}, t("familyCheckUpdate")),
|
|
5343
|
+
el("button", {
|
|
5344
|
+
type: "button",
|
|
5345
|
+
className: styles.toggle,
|
|
5346
|
+
style: { whiteSpace: "nowrap", flexShrink: 0 },
|
|
5347
|
+
disabled: annotationBusy === group.id,
|
|
5348
|
+
onClick: () => {
|
|
5349
|
+
// 一键启用已适配:只对组内有 adoptable 的成员解锁
|
|
5350
|
+
const ready = bucket
|
|
5351
|
+
.map((card) => card?.key ?? null)
|
|
5352
|
+
.filter((x) => x !== null);
|
|
5353
|
+
const targets = (state.status === "ready" ? state.data.entries : [])
|
|
5354
|
+
.filter((e) => members.includes(String(e.moduleName ?? "")) && e.adoptable !== null && e.adoptable !== undefined);
|
|
5355
|
+
if (targets.length === 0) { setMessage(t("gateNoAdoptable")); return; }
|
|
5356
|
+
Promise.all(targets.map((e) => call("/plugin-console/adapt-unlock", { entryId: e.entryId }).catch(() => null)))
|
|
5357
|
+
.then(() => { setMessage(t("familyUnlockAll") + ":" + targets.length); refresh(); });
|
|
5358
|
+
},
|
|
5359
|
+
}, t("familyUnlockAll")),
|
|
5360
|
+
el("button", {
|
|
5361
|
+
type: "button",
|
|
5362
|
+
className: styles.toggle,
|
|
5363
|
+
style: { display: "none" },
|
|
5364
|
+
title: t("annotationsPanelTitle"),
|
|
5365
|
+
onClick: () => { openAnnotationsPanel(); },
|
|
5366
|
+
}, t("annotationsBtn")));
|
|
5367
|
+
const memberChips = members.length === 0
|
|
5368
|
+
? el("span", { className: styles.status }, t("groupNoMembers"))
|
|
5369
|
+
: members.map((name) => el("span", { key: "ugm-" + name, className: styles.tag, "data-usergroup-member": "true", title: name }, name,
|
|
5370
|
+
el("button", {
|
|
5371
|
+
type: "button",
|
|
5372
|
+
className: styles.tag,
|
|
5373
|
+
"data-more": "true",
|
|
5374
|
+
title: t("groupMemberRemove"),
|
|
5375
|
+
"aria-label": t("groupMemberRemove"),
|
|
5376
|
+
disabled: annotationBusy === group.id,
|
|
5377
|
+
onClick: () => annotationWrite({ action: "group-assign", id: group.id, packageName: name, member: false }, t("groupUnassigned")),
|
|
5378
|
+
}, "×")));
|
|
5379
|
+
const addSelect = el("select", {
|
|
5380
|
+
value: "",
|
|
5381
|
+
"aria-label": "groupMemberAdd",
|
|
5382
|
+
disabled: annotationBusy === group.id || candidates.length === 0,
|
|
5383
|
+
style: { font: "inherit", padding: "2px 6px", maxWidth: 320 },
|
|
5384
|
+
onChange: (event) => {
|
|
5385
|
+
const picked = String(event.target.value ?? "");
|
|
5386
|
+
if (picked === "") return;
|
|
5387
|
+
annotationWrite({ action: "group-assign", id: group.id, packageName: picked }, t("groupAssigned").replace("{name}", String(group.name)));
|
|
5388
|
+
},
|
|
5389
|
+
},
|
|
5390
|
+
el("option", { value: "" }, candidates.length === 0 ? t("groupMemberNone") : t("groupMemberPick")),
|
|
5391
|
+
candidates.map((name) => el("option", { key: "ugc-" + name, value: name }, name)));
|
|
5392
|
+
const body = el("div", { style: { flexDirection: "column", gap: 6, display: "flex", marginTop: 2 } },
|
|
5393
|
+
el("div", { className: styles.annotRow }, memberChips, addSelect),
|
|
5394
|
+
bucket.length > 0
|
|
5395
|
+
? el("ul", { className: styles.list }, bucket)
|
|
5396
|
+
: members.length > 0
|
|
5397
|
+
? el("p", { className: styles.status, "data-in-family": "true" }, t("groupInFamilyNote"))
|
|
5398
|
+
: null);
|
|
5399
|
+
return el("div", {
|
|
5400
|
+
key: "usergroup-" + group.id,
|
|
5401
|
+
className: styles.detail + (fresh ? " " + styles.groupNew : ""),
|
|
5402
|
+
"data-usergroup": "true",
|
|
5403
|
+
"data-group-id": String(group.id),
|
|
5404
|
+
"data-new-group": fresh ? "true" : undefined,
|
|
5405
|
+
style: { border: "1px solid var(--dsw-alias-state-business-primary)", borderRadius: 10, padding: "8px 12px", gridColumn: "1 / -1" },
|
|
5406
|
+
},
|
|
5407
|
+
header,
|
|
5408
|
+
group.collapsed === true ? null : body);
|
|
5409
|
+
};
|
|
5410
|
+
const userGroupSectionEls = [];
|
|
5411
|
+
for (const group of userGroups) {
|
|
5412
|
+
const bucket = userGroupBuckets.get(group.id) ?? [];
|
|
5413
|
+
const members = Array.isArray(group.packages) ? group.packages : [];
|
|
5414
|
+
// ★ 用户 2026-10-26:分组框**永远画出来**。此前"成员全在官方 family 里就跳过"会让用户
|
|
5415
|
+
// 建完分组后在主列表什么都看不到(既没框也没入场动画),观感等于"点了没反应"。
|
|
5416
|
+
// 同一插件仍不重复渲染:那些成员留在官方 family 区块里,这里只给一句如实说明。
|
|
5417
|
+
userGroupSectionEls.push(userGroupFrame(group, bucket));
|
|
5418
|
+
}
|
|
5419
|
+
const rows = state.status === "ready" ? [...nonFamilyCards, ...familySectionEls, ...userGroupSectionEls, ...pendingCards] : [];
|
|
4675
5420
|
const marketShown = market === null || market.status !== "ready"
|
|
4676
5421
|
? []
|
|
4677
5422
|
: officialOnly
|
|
@@ -5225,7 +5970,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5225
5970
|
"aria-label": t("linksDevRootLabel"),
|
|
5226
5971
|
value: "",
|
|
5227
5972
|
disabled: linksPinBusy,
|
|
5228
|
-
onChange: (event) => { const picked = String(event.
|
|
5973
|
+
onChange: (event) => { const picked = String(event.target.value ?? ""); if (picked !== "") setLinksDir(devRootDraft(picked)); },
|
|
5229
5974
|
style: { flex: "0 1 300px", minWidth: 180, font: "inherit", padding: "4px 6px" },
|
|
5230
5975
|
},
|
|
5231
5976
|
el("option", { value: "" }, t("linksDevRootPick")),
|
|
@@ -5237,7 +5982,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5237
5982
|
value: linksDir,
|
|
5238
5983
|
disabled: linksPinBusy,
|
|
5239
5984
|
"aria-label": t("linksDirPlaceholder"),
|
|
5240
|
-
onChange: (event) => setLinksDir(String(event.
|
|
5985
|
+
onChange: (event) => setLinksDir(String(event.target.value ?? "")),
|
|
5241
5986
|
style: { flex: 1, minWidth: 220, font: "inherit", padding: "4px 8px" },
|
|
5242
5987
|
}),
|
|
5243
5988
|
// ★ 只在宿主**确实**有系统目录对话框时才渲染(web 端没有 ⇒ 不显示,不是灰掉)
|
|
@@ -5318,6 +6063,231 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5318
6063
|
el("div", { className: styles.rowTop, style: { justifyContent: "flex-end", gap: 8, marginTop: 10 } },
|
|
5319
6064
|
el("button", { type: "button", className: styles.toolItem, "data-busy": linksPanelBusy ? "true" : "false", onClick: () => { linksPanelLoad(); linksDevRootsLoad(); } }, linksPanelBusy ? t("linksPanelLoading") : t("linksPanelReload")))))
|
|
5320
6065
|
: null,
|
|
6066
|
+
annotationsPanelOpen
|
|
6067
|
+
? el("div", { className: styles.modalBackdrop, onClick: (event) => { if (event.target === event.currentTarget) setAnnotationsPanelOpen(false); } },
|
|
6068
|
+
el("div", { className: styles.modalCard, style: { maxWidth: 760, maxHeight: "82vh", overflow: "auto" } },
|
|
6069
|
+
el("div", { className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
6070
|
+
el("strong", { className: styles.name }, t("annotationsBtn")),
|
|
6071
|
+
el("button", { type: "button", className: styles.trashBtn, style: { fontSize: 14, padding: "4px 8px" }, title: t("closeModal"), "aria-label": t("closeModal"), onClick: () => setAnnotationsPanelOpen(false) }, "✕")),
|
|
6072
|
+
el("p", { className: styles.message }, t("annotationsPanelTitle")),
|
|
6073
|
+
annotations !== null && annotations.file !== null && annotations.file !== undefined
|
|
6074
|
+
? el("p", { className: styles.status }, String(annotations.file))
|
|
6075
|
+
: null,
|
|
6076
|
+
// 失败/降级一律有文字(绝不"点了一下没反应")
|
|
6077
|
+
annotationsNote !== null
|
|
6078
|
+
? el("p", { className: styles.message, "data-error": "true" }, annotationsNote)
|
|
6079
|
+
: null,
|
|
6080
|
+
// ── 分组管理:新建 / 重命名 / 删除(两段式)/ 撤销删除 ─────────────────
|
|
6081
|
+
el("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 6, paddingTop: 8, borderTop: "1px solid var(--dsw-alias-border-secondary, rgba(127,127,127,.18))" } },
|
|
6082
|
+
el("div", { className: styles.annotRow },
|
|
6083
|
+
el("input", {
|
|
6084
|
+
type: "text",
|
|
6085
|
+
value: groupDraftName,
|
|
6086
|
+
placeholder: t("groupCreatePlaceholder"),
|
|
6087
|
+
"aria-label": t("groupCreatePlaceholder"),
|
|
6088
|
+
disabled: annotationBusy !== null,
|
|
6089
|
+
style: { flex: 1, minWidth: 200, font: "inherit", padding: "4px 8px" },
|
|
6090
|
+
onChange: (event) => setGroupDraftName(String(event.target.value ?? "")),
|
|
6091
|
+
onKeyDown: (event) => { if (event.key === "Enter") { event.preventDefault(); createUserGroup(); } },
|
|
6092
|
+
}),
|
|
6093
|
+
el("button", { type: "button", className: styles.toggle, disabled: groupDraftName.trim() === "" || annotationBusy !== null, onClick: createUserGroup }, t("groupCreate"))),
|
|
6094
|
+
groupUndo !== null
|
|
6095
|
+
? el("div", { className: styles.annotRow },
|
|
6096
|
+
el("span", { className: styles.status, "data-pending": "true" }, t("groupDeleted").replace("{name}", String(groupUndo.group?.name ?? "")).replace("{n}", String(Array.isArray(groupUndo.group?.packages) ? groupUndo.group.packages.length : 0))),
|
|
6097
|
+
el("button", {
|
|
6098
|
+
type: "button",
|
|
6099
|
+
className: styles.toggle,
|
|
6100
|
+
disabled: annotationBusy !== null,
|
|
6101
|
+
onClick: () => {
|
|
6102
|
+
const record = groupUndo;
|
|
6103
|
+
setGroupUndo(null);
|
|
6104
|
+
annotationWrite({ action: "group-restore", group: record.group, index: record.index }, t("groupRestored").replace("{name}", String(record.group?.name ?? "")));
|
|
6105
|
+
},
|
|
6106
|
+
}, t("groupRestore")))
|
|
6107
|
+
: null,
|
|
6108
|
+
userGroups.length === 0
|
|
6109
|
+
? el("p", { className: styles.status }, t("groupEmptyHint"))
|
|
6110
|
+
: el("ul", { className: styles.list }, userGroups.map((group) => el("li", { key: "ug-" + group.id, className: styles.row },
|
|
6111
|
+
el("div", { className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
6112
|
+
el("strong", { className: styles.name, "data-group-name": String(group.id), style: { cursor: "pointer", borderBottom: "1px dotted currentColor" }, title: t("groupOpenHint"), onClick: () => { setPanelGroupOpen((prev) => (prev === group.id ? null : group.id)); setPanelGroupQuery(""); } }, String(group.name)),
|
|
6113
|
+
el("span", { className: styles.tag, "data-user-badge": "true" }, t("groupMembers").replace("{n}", String(Array.isArray(group.packages) ? group.packages.length : 0)))),
|
|
6114
|
+
el("div", { className: styles.annotRow },
|
|
6115
|
+
groupRenameEdit !== null && groupRenameEdit.id === group.id
|
|
6116
|
+
? el("div", { className: styles.annotRow },
|
|
6117
|
+
el("input", {
|
|
6118
|
+
type: "text",
|
|
6119
|
+
value: groupRenameEdit.draft,
|
|
6120
|
+
"aria-label": t("groupRename"),
|
|
6121
|
+
style: { font: "inherit", padding: "2px 6px" },
|
|
6122
|
+
onChange: (event) => setGroupRenameEdit({ id: group.id, draft: String(event.target.value ?? "") }),
|
|
6123
|
+
onKeyDown: (event) => {
|
|
6124
|
+
if (event.key === "Escape") { event.preventDefault(); setGroupRenameEdit(null); return; }
|
|
6125
|
+
if (event.key === "Enter") {
|
|
6126
|
+
event.preventDefault();
|
|
6127
|
+
const draft = groupRenameEdit.draft;
|
|
6128
|
+
setGroupRenameEdit(null);
|
|
6129
|
+
annotationWrite({ action: "group-rename", id: group.id, name: draft }, t("groupRenamed"));
|
|
6130
|
+
}
|
|
6131
|
+
},
|
|
6132
|
+
}),
|
|
6133
|
+
el("button", { type: "button", className: styles.toggle, onClick: () => setGroupRenameEdit(null) }, t("linksCancel")))
|
|
6134
|
+
: el("button", { type: "button", className: styles.toggle, disabled: annotationBusy === group.id, onClick: () => setGroupRenameEdit({ id: group.id, draft: String(group.name) }) }, t("groupRename")),
|
|
6135
|
+
groupDeleteArm === group.id
|
|
6136
|
+
? el("div", { className: styles.annotRow },
|
|
6137
|
+
el("span", { className: styles.status, "data-pending": "true" }, t("groupDeleteConfirm").replace("{name}", String(group.name))),
|
|
6138
|
+
el("button", { type: "button", className: styles.toggle, onClick: () => setGroupDeleteArm(null) }, t("linksCancel")),
|
|
6139
|
+
el("button", {
|
|
6140
|
+
type: "button",
|
|
6141
|
+
className: styles.toggle,
|
|
6142
|
+
disabled: annotationBusy === group.id,
|
|
6143
|
+
onClick: () => {
|
|
6144
|
+
setGroupDeleteArm(null);
|
|
6145
|
+
const memberCount = Array.isArray(group.packages) ? group.packages.length : 0;
|
|
6146
|
+
// 两段式:第一次点只是"上膛",这里才是真删(且**只解除归属**)
|
|
6147
|
+
annotationWrite({ action: "group-delete", id: group.id }, t("groupDeleted").replace("{name}", String(group.name)).replace("{n}", String(memberCount))).then((data) => {
|
|
6148
|
+
const removed = data?.deleted;
|
|
6149
|
+
if (removed !== null && removed !== undefined && removed.group !== null && removed.group !== undefined) setGroupUndo({ group: removed.group, index: Number.isFinite(removed.index) ? removed.index : null });
|
|
6150
|
+
});
|
|
6151
|
+
},
|
|
6152
|
+
}, t("confirmDelete")))
|
|
6153
|
+
: el("button", { type: "button", className: styles.trashBtn, title: t("groupRemove"), "aria-label": t("groupRemove"), disabled: annotationBusy === group.id, onClick: () => setGroupDeleteArm(group.id) }, "✕")))))),
|
|
6154
|
+
// ★ 2026-10-27:点分组名 → 展开「成员 + 一键更新 + 搜索添加(含备注)」
|
|
6155
|
+
(() => {
|
|
6156
|
+
const open = userGroups.find((g) => g.id === panelGroupOpen) ?? null;
|
|
6157
|
+
if (open === null) {
|
|
6158
|
+
return el("p", { className: styles.status, "data-panel-groupadd-hint": "true" }, t("groupOpenHint"));
|
|
6159
|
+
}
|
|
6160
|
+
const inGroup = new Set((Array.isArray(open.packages) ? open.packages : []).map((x) => String(x)));
|
|
6161
|
+
const noteOf = (nm) => {
|
|
6162
|
+
const rec = annotations !== null && annotations.plugins !== null && annotations.plugins !== undefined
|
|
6163
|
+
? annotations.plugins[nm] ?? null
|
|
6164
|
+
: null;
|
|
6165
|
+
return typeof rec?.note === "string" ? rec.note : "";
|
|
6166
|
+
};
|
|
6167
|
+
const q = panelGroupQuery.trim().toLowerCase();
|
|
6168
|
+
const pool = (state.status === "ready" ? state.data.entries : [])
|
|
6169
|
+
.map((e) => String(e.moduleName ?? ""))
|
|
6170
|
+
.filter((nm) => nm !== "" && !nm.startsWith("cordis:") && !inGroup.has(nm))
|
|
6171
|
+
// ★ 用户 2026-10-27:搜索要能命中**备注**(不只是包名)
|
|
6172
|
+
.filter((nm) => q === "" || nm.toLowerCase().includes(q) || noteOf(nm).toLowerCase().includes(q));
|
|
6173
|
+
const shown = pool.slice(0, panelGroupShow);
|
|
6174
|
+
const hidden = Math.max(0, pool.length - shown.length);
|
|
6175
|
+
const chips = [...inGroup].map((nm) => el("span", { key: "pgm-" + nm, className: styles.tag, "data-usergroup-member": "true" },
|
|
6176
|
+
nm,
|
|
6177
|
+
el("button", {
|
|
6178
|
+
type: "button",
|
|
6179
|
+
className: styles.tag,
|
|
6180
|
+
"data-more": "true",
|
|
6181
|
+
title: t("groupMemberRemove"),
|
|
6182
|
+
onClick: () => annotationWrite({ action: "group-assign", id: open.id, packageName: nm, member: false }, t("groupUnassigned")),
|
|
6183
|
+
}, "×")))
|
|
6184
|
+
const headerRow = el("div", { className: styles.annotRow },
|
|
6185
|
+
el("strong", { className: styles.name }, String(open.name)),
|
|
6186
|
+
el("span", { className: styles.status }, t("groupMembers").replace("{n}", String(inGroup.size))),
|
|
6187
|
+
el("button", {
|
|
6188
|
+
type: "button",
|
|
6189
|
+
className: styles.toggle,
|
|
6190
|
+
"data-group-update-all": String(open.id),
|
|
6191
|
+
title: t("groupUpdateAllTip"),
|
|
6192
|
+
onClick: () => {
|
|
6193
|
+
const members2 = [...inGroup];
|
|
6194
|
+
if (members2.length === 0) { setMessage(t("groupNoMembers")); return; }
|
|
6195
|
+
// 逐个串行:先查目标版本,再按那个版本更新(与单卡同一判据),门控照旧生效
|
|
6196
|
+
const step = (i, hit) => {
|
|
6197
|
+
if (i >= members2.length) {
|
|
6198
|
+
setMessage(t("groupUpdateAllDone").replace("{hit}", String(hit)).replace("{n}", String(members2.length)));
|
|
6199
|
+
return;
|
|
6200
|
+
}
|
|
6201
|
+
const nm = members2[i];
|
|
6202
|
+
setMessage(t("groupUpdateAllProgress").replace("{i}", String(i + 1)).replace("{n}", String(members2.length)) + " — " + nm);
|
|
6203
|
+
call("/plugin-console/check-update", { packageName: nm }).then((d) => {
|
|
6204
|
+
const target = typeof d?.target === "string" && d.target !== "" ? d.target : (typeof d?.latest === "string" ? d.latest : null);
|
|
6205
|
+
if (target !== null) startJob("", nm, "github", "plugin", true, null, target);
|
|
6206
|
+
step(i + 1, target !== null ? hit + 1 : hit);
|
|
6207
|
+
}, () => step(i + 1, hit));
|
|
6208
|
+
};
|
|
6209
|
+
step(0, 0);
|
|
6210
|
+
},
|
|
6211
|
+
}, t("groupUpdateAll")))
|
|
6212
|
+
const searchInput = el("input", {
|
|
6213
|
+
type: "search",
|
|
6214
|
+
value: panelGroupQuery,
|
|
6215
|
+
placeholder: t("groupAddSearchPlaceholder"),
|
|
6216
|
+
"aria-label": t("groupAddSearchPlaceholder"),
|
|
6217
|
+
style: { font: "inherit", padding: "4px 8px", width: "100%" },
|
|
6218
|
+
onChange: (event) => setPanelGroupQuery(String(event.target.value ?? "")),
|
|
6219
|
+
})
|
|
6220
|
+
const candRows = shown.map((nm) => {
|
|
6221
|
+
const note = noteOf(nm);
|
|
6222
|
+
return el("li", { key: "pgc-" + nm, className: styles.row },
|
|
6223
|
+
el("div", { className: styles.rowTop },
|
|
6224
|
+
el("strong", { className: styles.name, title: nm, style: { minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, nm),
|
|
6225
|
+
note !== ""
|
|
6226
|
+
? el("span", { style: { color: "var(--dsw-alias-state-business-primary)", fontWeight: 500, fontSize: ".9em", whiteSpace: "nowrap", maxWidth: "45%", overflow: "hidden", textOverflow: "ellipsis" } }, note)
|
|
6227
|
+
: null,
|
|
6228
|
+
el("button", {
|
|
6229
|
+
type: "button",
|
|
6230
|
+
className: styles.toggle,
|
|
6231
|
+
"data-group-add": nm,
|
|
6232
|
+
disabled: annotationBusy !== null,
|
|
6233
|
+
onClick: () => annotationWrite({ action: "group-assign", id: open.id, packageName: nm }, t("groupAssigned").replace("{name}", String(open.name))),
|
|
6234
|
+
}, "+ " + t("groupAddOne"))));
|
|
6235
|
+
})
|
|
6236
|
+
const candList = el("ul", { className: styles.list, "data-group-candidates": "true" },
|
|
6237
|
+
pool.length === 0
|
|
6238
|
+
? el("li", { className: styles.row }, el("span", { className: styles.status }, t("groupAddNoMatch")))
|
|
6239
|
+
: candRows)
|
|
6240
|
+
const moreBtn = hidden > 0
|
|
6241
|
+
? el("button", { type: "button", className: styles.toggle, "data-group-loadmore": "true", onClick: () => setPanelGroupShow((prev) => prev + 20) }, t("groupLoadMore").replace("{n}", String(hidden)))
|
|
6242
|
+
: null
|
|
6243
|
+
return el("div", { "data-panel-groupadd": String(open.id), style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 6 } },
|
|
6244
|
+
headerRow,
|
|
6245
|
+
el("div", { className: styles.annotRow }, chips.length > 0 ? chips : el("span", { className: styles.status }, t("groupNoMembers"))),
|
|
6246
|
+
searchInput,
|
|
6247
|
+
candList,
|
|
6248
|
+
moreBtn)
|
|
6249
|
+
})(),
|
|
6250
|
+
// ── 插件行:备注 / 徽章 / 分组(每行一套,与详情页同一个编辑块)───────────
|
|
6251
|
+
el("p", { className: styles.message, style: { marginTop: 8 } },
|
|
6252
|
+
annotations === null
|
|
6253
|
+
? t("annotationsEmpty")
|
|
6254
|
+
: t("annotationsStats")
|
|
6255
|
+
.replace("{p}", String(annotations.stats?.packages ?? 0))
|
|
6256
|
+
.replace("{b}", String(annotations.stats?.badges ?? 0))
|
|
6257
|
+
.replace("{g}", String(annotations.stats?.groups ?? 0))),
|
|
6258
|
+
// ★ 0.5.93(issue #22 ②,用户定稿):列表**默认收起**,只留一行摘要「插件列表(N)· 点此展开」;
|
|
6259
|
+
// 点开才渲染搜索框与插件行。查询词是受控 state ⇒ 边打边筛,且过滤不会清掉输入框里的字。
|
|
6260
|
+
el("button", {
|
|
6261
|
+
type: "button",
|
|
6262
|
+
className: styles.toggle,
|
|
6263
|
+
"aria-label": "annotationsListToggle",
|
|
6264
|
+
"aria-expanded": annotationsListOpen ? "true" : "false",
|
|
6265
|
+
"data-open": annotationsListOpen ? "true" : "false",
|
|
6266
|
+
onClick: () => setAnnotationsListOpen((prev) => !prev),
|
|
6267
|
+
}, (annotationsListOpen ? t("annotationsListHide") : t("annotationsListShow"))
|
|
6268
|
+
.replace("{n}", String(annotationListEntries.length))),
|
|
6269
|
+
annotationsListOpen === false
|
|
6270
|
+
? null
|
|
6271
|
+
: el("div", { style: { flexDirection: "column", gap: 8, display: "flex", marginTop: 6 } },
|
|
6272
|
+
el("input", {
|
|
6273
|
+
type: "text",
|
|
6274
|
+
value: annotationsQuery,
|
|
6275
|
+
placeholder: t("annotationsSearchPlaceholder"),
|
|
6276
|
+
"aria-label": "annotationsSearch",
|
|
6277
|
+
style: { font: "inherit", padding: "4px 8px", borderRadius: 6 },
|
|
6278
|
+
onChange: (event) => setAnnotationsQuery(String(event.target.value ?? "")),
|
|
6279
|
+
}),
|
|
6280
|
+
annotationListEntries.length === 0
|
|
6281
|
+
? el("p", { className: styles.status }, t("empty"))
|
|
6282
|
+
: annotationListFiltered.length === 0
|
|
6283
|
+
? el("p", { className: styles.status, "data-nomatch": "true" }, t("annotationsNoMatch"))
|
|
6284
|
+
: el("ul", { className: styles.list, "data-annotlist": "true" }, annotationListFiltered.map((entry) => el("li", { key: "an-" + entry.entryId, className: styles.row },
|
|
6285
|
+
el("div", { className: styles.rowTop },
|
|
6286
|
+
el("strong", { className: styles.name, title: entry.moduleName }, moduleShortName(entry.moduleName)),
|
|
6287
|
+
entry.extra === true ? el("span", { className: styles.tag, "data-user": "true" }, t("extraTag")) : null),
|
|
6288
|
+
el("div", { className: styles.meta }, el("code", null, String(entry.moduleName ?? ""))),
|
|
6289
|
+
annotationBlock(entry)))))))
|
|
6290
|
+
: null,
|
|
5321
6291
|
gateOpen
|
|
5322
6292
|
? el("div", { className: styles.modalBackdrop, onClick: (event) => { if (event.target === event.currentTarget) setGateOpen(false); } },
|
|
5323
6293
|
el("div", { className: styles.modalCard, style: { maxWidth: 520 } },
|
|
@@ -5424,7 +6394,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5424
6394
|
aiJob === null || (aiJob.status === "running" && aiJob.stage === "planning")
|
|
5425
6395
|
? el("div", { style: { flexDirection: "column", gap: "10px", display: "flex" } },
|
|
5426
6396
|
el("section", null,
|
|
5427
|
-
el("input", { type: "text", placeholder: t("aiEmpowerSrcTitle"), value: aiSource, disabled: aiRunning, onChange: (event) => setAiSource(event.
|
|
6397
|
+
el("input", { type: "text", placeholder: t("aiEmpowerSrcTitle"), value: aiSource, disabled: aiRunning, onChange: (event) => setAiSource(event.target.value), onKeyDown: (event) => { if (event.key === "Enter" && !aiRunning) aiStartPlan(); }, style: { width: "100%", boxSizing: "border-box", font: "inherit", padding: "6px 10px", borderRadius: 8, border: "1px solid var(--dsw-alias-border-l2)", background: "var(--dsw-alias-bg-layer-1)", color: "var(--dsw-alias-label-primary)" } }),
|
|
5428
6398
|
el("div", { className: styles.rowTop },
|
|
5429
6399
|
el("button", { type: "button", className: styles.toggle, disabled: aiRunning || aiSource.trim() === "", onClick: aiStartPlan }, t("aiEmpowerLabel")),
|
|
5430
6400
|
aiRunning ? el("span", { className: styles.message }, t("aiEmpowerWaiting") + (aiJob?.source ? "(" + aiJob.source + ")" : "")) : null,
|
|
@@ -5447,7 +6417,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5447
6417
|
el("div", { style: { flexDirection: "column", gap: "6px", display: "flex", maxHeight: 220, overflowY: "auto" } },
|
|
5448
6418
|
(aiJob.steps ?? []).map((s) =>
|
|
5449
6419
|
el("label", { key: s.index, className: styles.consentRemember, style: { alignItems: "flex-start" } },
|
|
5450
|
-
el("input", { type: "checkbox", checked: aiSelected[s.index] === true, onChange: (event) => setAiSelected((prev) => ({ ...prev, [s.index]: event.
|
|
6420
|
+
el("input", { type: "checkbox", checked: aiSelected[s.index] === true, onChange: (event) => setAiSelected((prev) => ({ ...prev, [s.index]: event.target.checked })) }),
|
|
5451
6421
|
el("span", null, (s.index + 1) + ". [" + s.action + "] " + (s.description ?? "") + (s.path ? " → " + s.path : "")))),
|
|
5452
6422
|
el("div", { className: styles.rowTop },
|
|
5453
6423
|
el("button", { type: "button", className: styles.toggle, onClick: aiRun }, t("aiEmpowerRun")),
|
|
@@ -5468,10 +6438,10 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5468
6438
|
el("p", { className: styles.message }, repoLandTitleText()),
|
|
5469
6439
|
el("div", { className: styles.rowTop, style: { flexWrap: "wrap" } },
|
|
5470
6440
|
el("span", { className: styles.message }, t("repoLandDirLabel") + ":" + (repoLandDir || "—")),
|
|
5471
|
-
el("input", { type: "text", value: repoLandDirInput, onChange: (event) => setRepoLandDirInput(event.
|
|
6441
|
+
el("input", { type: "text", value: repoLandDirInput, onChange: (event) => setRepoLandDirInput(event.target.value), style: { flex: 1, minWidth: 120, font: "inherit", padding: "4px 8px", borderRadius: 6, border: "1px solid var(--dsw-alias-border-l2)", background: "var(--dsw-alias-bg-layer-1)", color: "var(--dsw-alias-label-primary)" } }),
|
|
5472
6442
|
el("button", { type: "button", className: styles.toggle, onClick: repoLandSaveDir }, t("repoLandSaveDir"))),
|
|
5473
6443
|
el("section", null,
|
|
5474
|
-
el("input", { type: "text", placeholder: t("repoLandPlaceholder"), value: repoLandInput, disabled: repoLandBusy, onChange: (event) => setRepoLandInput(event.
|
|
6444
|
+
el("input", { type: "text", placeholder: t("repoLandPlaceholder"), value: repoLandInput, disabled: repoLandBusy, onChange: (event) => setRepoLandInput(event.target.value), onKeyDown: (event) => { if (event.key === "Enter" && !repoLandBusy) repoLandStart(); }, style: { width: "100%", boxSizing: "border-box", font: "inherit", padding: "6px 10px", borderRadius: 8, border: "1px solid var(--dsw-alias-border-l2)", background: "var(--dsw-alias-bg-layer-1)", color: "var(--dsw-alias-label-primary)" } }),
|
|
5475
6445
|
el("div", { className: styles.rowTop },
|
|
5476
6446
|
el("button", { type: "button", className: styles.toggle, disabled: repoLandBusy || repoLandInput.trim() === "", onClick: repoLandStart }, repoLandBusy ? t("repoLandBusy") : t("repoLandLabel"))),
|
|
5477
6447
|
repoLandResult !== null
|
|
@@ -5728,7 +6698,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5728
6698
|
type: "password",
|
|
5729
6699
|
placeholder: t("githubTokenPlaceholder"),
|
|
5730
6700
|
value: ghToken,
|
|
5731
|
-
onChange: (event) => setGhToken(event.
|
|
6701
|
+
onChange: (event) => setGhToken(event.target.value),
|
|
5732
6702
|
style: { width: "100%", boxSizing: "border-box" },
|
|
5733
6703
|
}),
|
|
5734
6704
|
el("button", { type: "button", className: styles.toggle, disabled: ghLoginBusy || ghToken.trim() === "", onClick: githubLogin }, t("githubLoginBtn")))
|
|
@@ -5811,6 +6781,16 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5811
6781
|
toolbarOpen
|
|
5812
6782
|
? el("div", { className: styles.toolbarGroup, style: (dragPos ?? toolbarPos) !== null ? { left: (dragPos ?? toolbarPos).left, top: (dragPos ?? toolbarPos).top + 36, right: "auto" } : undefined },
|
|
5813
6783
|
|
|
6784
|
+
el("button", {
|
|
6785
|
+
type: "button",
|
|
6786
|
+
className: styles.toolItem,
|
|
6787
|
+
"data-active": annotationsPanelOpen ? "true" : "false",
|
|
6788
|
+
title: t("annotationsPanelTitle"),
|
|
6789
|
+
"aria-label": t("annotationsBtn"),
|
|
6790
|
+
// issue #22 ②③:打开即读一次(注解是独立文件,别处改了也能看到最新的);
|
|
6791
|
+
// ★ 0.5.93:打开时列表回到**收起**状态(见 openAnnotationsPanel)
|
|
6792
|
+
onClick: () => toggleAnnotationsPanel(),
|
|
6793
|
+
}, t("annotationsBtn") + (annotations !== null && annotations.stats !== null && annotations.stats !== undefined && annotations.stats.packages > 0 ? " " + annotations.stats.packages : "")),
|
|
5814
6794
|
hostedByShell ? null : el("button", { // ★ 0.5.84:桌面端(shell 托管)不显示;web 端保留且可用
|
|
5815
6795
|
type: "button",
|
|
5816
6796
|
className: styles.toolItem,
|
|
@@ -5965,7 +6945,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5965
6945
|
: t("searchPlaceholderOther").replace("{source}", sourceDisplayName(searchSource))),
|
|
5966
6946
|
"aria-label": t("search"),
|
|
5967
6947
|
onChange: (event) => {
|
|
5968
|
-
const v = event.
|
|
6948
|
+
const v = event.target.value;
|
|
5969
6949
|
setQuery(v);
|
|
5970
6950
|
try { localStorage.setItem("pc-market-query", v); } catch {}
|
|
5971
6951
|
},
|
|
@@ -6183,6 +7163,49 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6183
7163
|
el("h3", null, mode === "skills" ? t("installedSkillsTitle") : t("installedTitle")),
|
|
6184
7164
|
mode === "plugins"
|
|
6185
7165
|
? el("button", { type: "button", className: styles.toggle, onClick: () => setInstalledSearchOpen((v) => !v) }, t("search"))
|
|
7166
|
+
: null,
|
|
7167
|
+
// ★ issue #22 ①:一键检测全部更新(**只有图标**的圆按钮 ↑↑↑,与「搜索」同高)。
|
|
7168
|
+
// 三态都在按钮上:idle(可点)/ checking(转圈 + 禁用)/ done(描边高亮,再点是重查)。
|
|
7169
|
+
// 判据全在服务端(唯一判据 + 并发上限),这里只负责"问一句、如实显示"。
|
|
7170
|
+
mode === "plugins"
|
|
7171
|
+
? el("button", {
|
|
7172
|
+
type: "button",
|
|
7173
|
+
className: styles.iconBtn,
|
|
7174
|
+
title: batchCheck !== null && batchCheck.status === "checking"
|
|
7175
|
+
? t("checkingAllUpdates").replace("{n}", String(batchCheck.total ?? 0))
|
|
7176
|
+
: t("checkAllUpdates") + ":" + t("checkAllUpdatesHint").replace("{n}", String(state.status === "ready" ? state.data.entries.length : 0)),
|
|
7177
|
+
"aria-label": t("checkAllUpdates"),
|
|
7178
|
+
"data-state": batchCheck !== null && batchCheck.status === "checking" ? "checking" : (batchCheck !== null && batchCheck.status === "ready" ? "done" : "idle"),
|
|
7179
|
+
disabled: (batchCheck !== null && batchCheck.status === "checking") || state.status !== "ready",
|
|
7180
|
+
onClick: checkAllUpdates,
|
|
7181
|
+
}, batchCheck !== null && batchCheck.status === "checking"
|
|
7182
|
+
? el("span", { className: styles.spinner })
|
|
7183
|
+
: el("svg", { width: 13, height: 13, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.2, strokeLinecap: "round", strokeLinejoin: "round" },
|
|
7184
|
+
el("path", { d: "M5 20V7" }), el("polyline", { points: "3 9 5 7 7 9" }),
|
|
7185
|
+
el("path", { d: "M12 20V7" }), el("polyline", { points: "10 9 12 7 14 9" }),
|
|
7186
|
+
el("path", { d: "M19 20V7" }), el("polyline", { points: "17 9 19 7 21 9" })))
|
|
7187
|
+
: null,
|
|
7188
|
+
// 结果态:可更新 > 0 ⇒ 可点的筛选按钮(同一个筛选的开关);可更新 = 0 ⇒ **只读**小结,
|
|
7189
|
+
// 不做成按钮(否则就是"点了一下列表变空"的死按钮)。
|
|
7190
|
+
mode === "plugins" && batchCheck !== null && batchCheck.status === "ready" && batchCheck.updatable > 0
|
|
7191
|
+
? el("button", {
|
|
7192
|
+
type: "button",
|
|
7193
|
+
className: styles.extraBtn,
|
|
7194
|
+
title: t("batchUpdateResultTip")
|
|
7195
|
+
.replace("{n}", String(batchCheck.updatable))
|
|
7196
|
+
.replace("{m}", String(Math.max(0, batchCheck.checked - batchCheck.updatable - batchCheck.unknown)))
|
|
7197
|
+
.replace("{k}", String(batchCheck.unknown))
|
|
7198
|
+
.replace("{at}", new Date(batchCheck.at).toLocaleTimeString()),
|
|
7199
|
+
"aria-pressed": onlyUpdatable ? "true" : "false",
|
|
7200
|
+
onClick: () => setOnlyUpdatable((v) => !v),
|
|
7201
|
+
}, t("batchUpdateOnly") + " · " + batchCheck.updatable + (onlyUpdatable ? " ✓" : ""))
|
|
7202
|
+
: null,
|
|
7203
|
+
mode === "plugins" && batchCheck !== null && batchCheck.status === "ready" && batchCheck.updatable === 0
|
|
7204
|
+
? el("span", { className: styles.tag, "data-enabled": "true", title: t("batchUpdateResultTip").replace("{n}", "0").replace("{m}", String(Math.max(0, batchCheck.checked - batchCheck.unknown))).replace("{k}", String(batchCheck.unknown)).replace("{at}", new Date(batchCheck.at).toLocaleTimeString()) },
|
|
7205
|
+
t("checkAllUpdateNone") + (batchCheck.unknown > 0 ? "(" + t("batchUpdateUnknown") + " " + batchCheck.unknown + ")" : ""))
|
|
7206
|
+
: null,
|
|
7207
|
+
mode === "plugins" && batchCheck !== null && batchCheck.status === "error"
|
|
7208
|
+
? el("span", { className: styles.tag, "data-pending": "true", title: String(batchCheck.error ?? "") }, t("checkAllUpdateFailed"))
|
|
6186
7209
|
: null),
|
|
6187
7210
|
mode === "plugins" && installedSearchOpen
|
|
6188
7211
|
? el("div", { className: styles.search },
|
|
@@ -6193,7 +7216,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6193
7216
|
placeholder: t("searchInstalledPlaceholder"),
|
|
6194
7217
|
"aria-label": t("searchInstalledPlaceholder"),
|
|
6195
7218
|
onChange: (event) => {
|
|
6196
|
-
const v = event.
|
|
7219
|
+
const v = event.target.value;
|
|
6197
7220
|
setInstalledQuery(v);
|
|
6198
7221
|
try { localStorage.setItem("pc-market-installed-q", v); } catch {}
|
|
6199
7222
|
},
|
|
@@ -6273,9 +7296,13 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6273
7296
|
? el("p", { className: styles.status }, t("empty"))
|
|
6274
7297
|
: installedQueryNorm !== "" && rows.length === 0
|
|
6275
7298
|
? el("p", { className: styles.status }, t("noMatch"))
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
7299
|
+
// issue #22 ①:把「仅看可更新」筛空的话必须说清楚**是筛空的**,
|
|
7300
|
+
// 否则用户看到空列表会以为插件没了(并给出取消筛选的办法)。
|
|
7301
|
+
: onlyUpdatable && rows.length === 0
|
|
7302
|
+
? el("p", { className: styles.status }, t("batchUpdateFilterEmpty"))
|
|
7303
|
+
: extraOnly && rows.length === 0
|
|
7304
|
+
? el("p", { className: styles.status }, t("extraEmpty"))
|
|
7305
|
+
: el("ul", { className: styles.list }, rows)),
|
|
6279
7306
|
showBackTop
|
|
6280
7307
|
? el("button", {
|
|
6281
7308
|
type: "button",
|
|
@@ -6323,7 +7350,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6323
7350
|
type: "checkbox",
|
|
6324
7351
|
checked: aiRemember,
|
|
6325
7352
|
onChange: (event) => {
|
|
6326
|
-
const checked = event.
|
|
7353
|
+
const checked = event.target.checked;
|
|
6327
7354
|
setAiRemember(checked);
|
|
6328
7355
|
try { localStorage.setItem("pc-ai-remember", checked ? "1" : "0"); } catch {}
|
|
6329
7356
|
},
|
|
@@ -6351,8 +7378,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6351
7378
|
...sourcesData.registries.map((src) =>
|
|
6352
7379
|
editReg !== null && editReg.id === src.id
|
|
6353
7380
|
? el("div", { key: src.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
6354
|
-
el("input", { type: "text", value: editReg.name, onChange: (event) => setEditReg({ ...editReg, name: event.
|
|
6355
|
-
el("input", { type: "text", value: editReg.url, onChange: (event) => setEditReg({ ...editReg, url: event.
|
|
7381
|
+
el("input", { type: "text", value: editReg.name, onChange: (event) => setEditReg({ ...editReg, name: event.target.value }), style: { flex: 1, minWidth: 80 } }),
|
|
7382
|
+
el("input", { type: "text", value: editReg.url, onChange: (event) => setEditReg({ ...editReg, url: event.target.value }), style: { flex: 2, minWidth: 120 } }),
|
|
6356
7383
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: saveEditReg }, t("saveSource")),
|
|
6357
7384
|
el("button", { type: "button", className: styles.toggle, onClick: () => setEditReg(null) }, t("cancelEdit")))
|
|
6358
7385
|
: el("div", { key: src.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
@@ -6378,8 +7405,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6378
7405
|
src.primary ? null : el("button", { type: "button", className: styles.toggle, title: t("setPrimaryHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "set-primary", id: src.id }) }, t("setPrimary")),
|
|
6379
7406
|
el("button", { type: "button", className: styles.toggle, title: t("editSourceHint"), disabled: sourcesBusy, onClick: () => setEditReg({ id: src.id, name: src.name, url: src.url }) }, t("editSource"))))),
|
|
6380
7407
|
el("div", { className: styles.rowTop },
|
|
6381
|
-
el("input", { type: "text", placeholder: t("sourceName"), value: sourceName, onChange: (event) => setSourceName(event.
|
|
6382
|
-
el("input", { type: "text", placeholder: t("sourceUrl"), value: sourceUrl, onChange: (event) => setSourceUrl(event.
|
|
7408
|
+
el("input", { type: "text", placeholder: t("sourceName"), value: sourceName, onChange: (event) => setSourceName(event.target.value), style: { flex: 1, minWidth: 0 } }),
|
|
7409
|
+
el("input", { type: "text", placeholder: t("sourceUrl"), value: sourceUrl, onChange: (event) => setSourceUrl(event.target.value), style: { flex: 2, minWidth: 0 } }),
|
|
6383
7410
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: addSource }, t("addSource"))),
|
|
6384
7411
|
el("div", { className: styles.rowTop },
|
|
6385
7412
|
el("button", {
|
|
@@ -6401,8 +7428,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6401
7428
|
(sourcesData.indexSources ?? []).map((s) =>
|
|
6402
7429
|
editIndex !== null && editIndex.id === s.id
|
|
6403
7430
|
? el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
6404
|
-
el("input", { type: "text", value: editIndex.name, onChange: (event) => setEditIndex({ ...editIndex, name: event.
|
|
6405
|
-
el("input", { type: "text", value: editIndex.url, onChange: (event) => setEditIndex({ ...editIndex, url: event.
|
|
7431
|
+
el("input", { type: "text", value: editIndex.name, onChange: (event) => setEditIndex({ ...editIndex, name: event.target.value }), style: { flex: 1, minWidth: 80 } }),
|
|
7432
|
+
el("input", { type: "text", value: editIndex.url, onChange: (event) => setEditIndex({ ...editIndex, url: event.target.value }), style: { flex: 2, minWidth: 120 } }),
|
|
6406
7433
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: saveEditIndex }, t("saveSource")),
|
|
6407
7434
|
el("button", { type: "button", className: styles.toggle, onClick: () => setEditIndex(null) }, t("cancelEdit")))
|
|
6408
7435
|
: el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
@@ -6414,8 +7441,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6414
7441
|
el("button", { type: "button", className: styles.toggle, title: t("editSourceHint"), disabled: sourcesBusy, onClick: () => setEditIndex({ id: s.id, name: s.name, url: s.url }) }, t("editSource")),
|
|
6415
7442
|
el("button", { type: "button", className: styles.toggle, title: t("removeSourceHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "remove-index", id: s.id }) }, t("removeSource"))))),
|
|
6416
7443
|
el("div", { className: styles.rowTop },
|
|
6417
|
-
el("input", { type: "text", placeholder: t("sourceName"), value: indexSourceName, onChange: (event) => setIndexSourceName(event.
|
|
6418
|
-
el("input", { type: "text", placeholder: t("indexUrlPlaceholder"), value: indexSourceUrl, onChange: (event) => setIndexSourceUrl(event.
|
|
7444
|
+
el("input", { type: "text", placeholder: t("sourceName"), value: indexSourceName, onChange: (event) => setIndexSourceName(event.target.value), style: { flex: 1, minWidth: 0 } }),
|
|
7445
|
+
el("input", { type: "text", placeholder: t("indexUrlPlaceholder"), value: indexSourceUrl, onChange: (event) => setIndexSourceUrl(event.target.value), style: { flex: 2, minWidth: 0 } }),
|
|
6419
7446
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: addIndexSource }, t("addIndexSource"))),
|
|
6420
7447
|
el("div", { className: styles.rowTop },
|
|
6421
7448
|
el("button", {
|
|
@@ -6433,8 +7460,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6433
7460
|
(sourcesData.gitSources ?? []).map((s) =>
|
|
6434
7461
|
editGit !== null && editGit.id === s.id
|
|
6435
7462
|
? el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
6436
|
-
el("input", { type: "text", value: editGit.name, onChange: (event) => setEditGit({ ...editGit, name: event.
|
|
6437
|
-
el("input", { type: "text", value: editGit.urlTemplate, onChange: (event) => setEditGit({ ...editGit, urlTemplate: event.
|
|
7463
|
+
el("input", { type: "text", value: editGit.name, onChange: (event) => setEditGit({ ...editGit, name: event.target.value }), style: { flex: 1, minWidth: 80 } }),
|
|
7464
|
+
el("input", { type: "text", value: editGit.urlTemplate, onChange: (event) => setEditGit({ ...editGit, urlTemplate: event.target.value }), style: { flex: 2, minWidth: 120 } }),
|
|
6438
7465
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: saveEditGit }, t("saveSource")),
|
|
6439
7466
|
el("button", { type: "button", className: styles.toggle, onClick: () => setEditGit(null) }, t("cancelEdit")))
|
|
6440
7467
|
: el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
@@ -6446,8 +7473,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6446
7473
|
el("button", { type: "button", className: styles.toggle, title: t("editSourceHint"), disabled: sourcesBusy, onClick: () => setEditGit({ id: s.id, name: s.name, urlTemplate: s.urlTemplate }) }, t("editSource")),
|
|
6447
7474
|
el("button", { type: "button", className: styles.toggle, title: t("removeSourceHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "remove-git", id: s.id }) }, t("removeSource"))))),
|
|
6448
7475
|
el("div", { className: styles.rowTop },
|
|
6449
|
-
el("input", { type: "text", placeholder: t("sourceName"), value: gitSourceName, onChange: (event) => setGitSourceName(event.
|
|
6450
|
-
el("input", { type: "text", placeholder: t("gitUrlPlaceholder"), value: gitSourceUrl, onChange: (event) => setGitSourceUrl(event.
|
|
7476
|
+
el("input", { type: "text", placeholder: t("sourceName"), value: gitSourceName, onChange: (event) => setGitSourceName(event.target.value), style: { flex: 1, minWidth: 0 } }),
|
|
7477
|
+
el("input", { type: "text", placeholder: t("gitUrlPlaceholder"), value: gitSourceUrl, onChange: (event) => setGitSourceUrl(event.target.value), style: { flex: 2, minWidth: 0 } }),
|
|
6451
7478
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: addGitSource }, t("addGitSource"))),
|
|
6452
7479
|
]),
|
|
6453
7480
|
accSection("archive", t("archiveSourcesTitle"), [
|
|
@@ -6455,8 +7482,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6455
7482
|
(sourcesData.archiveSources ?? []).map((s) =>
|
|
6456
7483
|
editArchive !== null && editArchive.id === s.id
|
|
6457
7484
|
? el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
6458
|
-
el("input", { type: "text", value: editArchive.name, onChange: (event) => setEditArchive({ ...editArchive, name: event.
|
|
6459
|
-
el("input", { type: "text", value: editArchive.urlTemplate, onChange: (event) => setEditArchive({ ...editArchive, urlTemplate: event.
|
|
7485
|
+
el("input", { type: "text", value: editArchive.name, onChange: (event) => setEditArchive({ ...editArchive, name: event.target.value }), style: { flex: 1, minWidth: 80 } }),
|
|
7486
|
+
el("input", { type: "text", value: editArchive.urlTemplate, onChange: (event) => setEditArchive({ ...editArchive, urlTemplate: event.target.value }), style: { flex: 2, minWidth: 120 } }),
|
|
6460
7487
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: saveEditArchive }, t("saveSource")),
|
|
6461
7488
|
el("button", { type: "button", className: styles.toggle, onClick: () => setEditArchive(null) }, t("cancelEdit")))
|
|
6462
7489
|
: el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
@@ -6468,8 +7495,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6468
7495
|
el("button", { type: "button", className: styles.toggle, title: t("editSourceHint"), disabled: sourcesBusy, onClick: () => setEditArchive({ id: s.id, name: s.name, urlTemplate: s.urlTemplate }) }, t("editSource")),
|
|
6469
7496
|
el("button", { type: "button", className: styles.toggle, title: t("removeSourceHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "remove-archive", id: s.id }) }, t("removeSource"))))),
|
|
6470
7497
|
el("div", { className: styles.rowTop },
|
|
6471
|
-
el("input", { type: "text", placeholder: t("sourceName"), value: archiveSourceName, onChange: (event) => setArchiveSourceName(event.
|
|
6472
|
-
el("input", { type: "text", placeholder: t("archiveUrlPlaceholder"), value: archiveSourceUrl, onChange: (event) => setArchiveSourceUrl(event.
|
|
7498
|
+
el("input", { type: "text", placeholder: t("sourceName"), value: archiveSourceName, onChange: (event) => setArchiveSourceName(event.target.value), style: { flex: 1, minWidth: 0 } }),
|
|
7499
|
+
el("input", { type: "text", placeholder: t("archiveUrlPlaceholder"), value: archiveSourceUrl, onChange: (event) => setArchiveSourceUrl(event.target.value), style: { flex: 2, minWidth: 0 } }),
|
|
6473
7500
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: addArchiveSource }, t("addArchiveSource"))),
|
|
6474
7501
|
]),
|
|
6475
7502
|
accSection("search", t("searchSourcesTitle"), [
|
|
@@ -6486,14 +7513,14 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6486
7513
|
? el("button", { type: "button", className: styles.toggle, title: t("removeSourceHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "remove-search", id: s.id }) }, t("removeSource"))
|
|
6487
7514
|
: null)),
|
|
6488
7515
|
el("div", { className: styles.rowTop },
|
|
6489
|
-
el("input", { type: "text", placeholder: t("sourceName"), value: searchSourceName, onChange: (event) => setSearchSourceName(event.
|
|
6490
|
-
el("input", { type: "text", placeholder: t("searchUrlPlaceholder"), value: searchSourceUrl, onChange: (event) => setSearchSourceUrl(event.
|
|
7516
|
+
el("input", { type: "text", placeholder: t("sourceName"), value: searchSourceName, onChange: (event) => setSearchSourceName(event.target.value), style: { flex: 1, minWidth: 0 } }),
|
|
7517
|
+
el("input", { type: "text", placeholder: t("searchUrlPlaceholder"), value: searchSourceUrl, onChange: (event) => setSearchSourceUrl(event.target.value), style: { flex: 2, minWidth: 0 } }),
|
|
6491
7518
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: addSearchSource }, t("addSearchSource"))),
|
|
6492
7519
|
el("textarea", {
|
|
6493
7520
|
rows: 2,
|
|
6494
7521
|
placeholder: t("headersPlaceholder"),
|
|
6495
7522
|
value: searchSourceHeaders,
|
|
6496
|
-
onChange: (event) => setSearchSourceHeaders(event.
|
|
7523
|
+
onChange: (event) => setSearchSourceHeaders(event.target.value),
|
|
6497
7524
|
style: { width: "100%", boxSizing: "border-box", font: "inherit", resize: "vertical" },
|
|
6498
7525
|
}),
|
|
6499
7526
|
]),
|
|
@@ -6505,8 +7532,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6505
7532
|
? t("giteeLoggedIn") + (giteeStatus.login || "—")
|
|
6506
7533
|
: (giteeStatus !== null && giteeStatus.clientConfigured ? t("giteeSetupHint") : t("giteeLoginPrompt")))),
|
|
6507
7534
|
el("div", { className: styles.rowTop },
|
|
6508
|
-
el("input", { type: "text", placeholder: t("giteeClientId"), value: giteeClientId, onChange: (event) => setGiteeClientId(event.
|
|
6509
|
-
el("input", { type: "password", placeholder: t("giteeClientSecret"), value: giteeClientSecret, onChange: (event) => setGiteeClientSecret(event.
|
|
7535
|
+
el("input", { type: "text", placeholder: t("giteeClientId"), value: giteeClientId, onChange: (event) => setGiteeClientId(event.target.value), style: { flex: 1, minWidth: 0 } }),
|
|
7536
|
+
el("input", { type: "password", placeholder: t("giteeClientSecret"), value: giteeClientSecret, onChange: (event) => setGiteeClientSecret(event.target.value), style: { flex: 2, minWidth: 0 } }),
|
|
6510
7537
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy || !giteeClientId.trim() || !giteeClientSecret.trim(), onClick: saveGiteeSetup }, t("giteeSave"))),
|
|
6511
7538
|
el("div", { className: styles.rowTop },
|
|
6512
7539
|
el("button", { type: "button", className: styles.toggle, disabled: giteeStatus !== null && !giteeStatus.clientConfigured, onClick: startGiteeOauth }, t("giteeLoginBtn")),
|