@noob-stupid/dsh-plugin-console 0.5.92 → 0.5.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +1085 -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,288 @@ 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
|
+
/** 某个包属于哪个用户分组(一个包最多一个分组;服务端保证 assign 会自动移出旧的)。 */
|
|
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
|
+
const createUserGroup = () => {
|
|
4611
|
+
const name = groupDraftName.trim();
|
|
4612
|
+
if (name === "") return;
|
|
4613
|
+
// ③:新建只走**回车/点按钮**这一条明确动作(绝不边打边建);成功后用服务端返回的
|
|
4614
|
+
// group.id 标一下"刚建好"(只为了在主列表的横框上播一次入场动画,数据仍是服务端那份)。
|
|
4615
|
+
setGroupDraftName("");
|
|
4616
|
+
annotationWrite({ action: "group-create", name }, t("groupCreated").replace("{name}", name)).then((data) => {
|
|
4617
|
+
const id = data !== null && data !== undefined && data.group !== null && data.group !== undefined
|
|
4618
|
+
? String(data.group.id ?? "")
|
|
4619
|
+
: "";
|
|
4620
|
+
if (id === "") return;
|
|
4621
|
+
setGroupJustCreated(id);
|
|
4622
|
+
try {
|
|
4623
|
+
// 动画只有 220ms;1.2s 后把这个标记撤掉,避免以后重渲染时又播一次
|
|
4624
|
+
window.setTimeout(() => setGroupJustCreated((prev) => (prev === id ? null : prev)), 1200);
|
|
4625
|
+
} catch {}
|
|
4626
|
+
});
|
|
4627
|
+
};
|
|
4628
|
+
/**
|
|
4629
|
+
* 打开「备注插件」面板(用户定稿:面板打开时插件列表**收起**,只留一行摘要)。
|
|
4630
|
+
* 每次打开都回到收起 + 空查询 —— 不给"上次展开到一半"的残留状态。
|
|
4631
|
+
*/
|
|
4632
|
+
const openAnnotationsPanel = () => {
|
|
4633
|
+
setAnnotationsListOpen(false);
|
|
4634
|
+
setAnnotationsQuery("");
|
|
4635
|
+
setAnnotationsPanelOpen(true);
|
|
4636
|
+
annotationsLoad();
|
|
4637
|
+
};
|
|
4638
|
+
/** 工具栏那个按钮是**开关**:开着再点就关掉("点了没反应"算缺陷)。 */
|
|
4639
|
+
const toggleAnnotationsPanel = () => {
|
|
4640
|
+
if (annotationsPanelOpen) { setAnnotationsPanelOpen(false); return; }
|
|
4641
|
+
openAnnotationsPanel();
|
|
4642
|
+
};
|
|
4643
|
+
// issue #22 ②:注解面板里的插件行 —— 默认收起;展开后按 包名 / rowId / 已有备注内容 即时过滤。
|
|
4644
|
+
// 查询词只读不写 ⇒ 边打边筛的过程中用户正在输入的内容**不会被清掉**。
|
|
4645
|
+
const annotationListEntries = state.status === "ready" ? state.data.entries : [];
|
|
4646
|
+
const annotationsQueryNorm = annotationsQuery.trim().toLowerCase();
|
|
4647
|
+
const annotationListFiltered = annotationsQueryNorm === ""
|
|
4648
|
+
? annotationListEntries
|
|
4649
|
+
: annotationListEntries.filter((entry) => [entry.moduleName, entry.rowId, annotationOf(entry)?.note]
|
|
4650
|
+
.some((value) => String(value ?? "").toLowerCase().includes(annotationsQueryNorm)));
|
|
4651
|
+
/**
|
|
4652
|
+
* 用户注解编辑块 —— 详情页与「备注插件」面板**共用同一份**(两处交互必须一模一样)。
|
|
4653
|
+
* · 备注:点一下就地编辑,回车保存、Esc 取消;存空 = 清空(清空后**不占位**)。
|
|
4654
|
+
* · 徽章:∪ 追加(系统徽章在卡片上是另一组,**绝不替换、绝不合并**);× 删除后可撤销一次。
|
|
4655
|
+
* · 没有备注也没有徽章时:只显示一个「+ 加备注」入口(用户定稿的那一种,不显示空壳)。
|
|
4656
|
+
*/
|
|
4657
|
+
const annotationBlock = (entry) => {
|
|
4658
|
+
const key = annotationKeyOf(entry);
|
|
4659
|
+
const packageName = String(entry?.moduleName ?? key);
|
|
4660
|
+
const record = annotationOf(entry);
|
|
4661
|
+
const note = typeof record?.note === "string" ? record.note : "";
|
|
4662
|
+
const badges = Array.isArray(record?.badges) ? record.badges : [];
|
|
4663
|
+
const group = groupOfPackage(packageName);
|
|
4664
|
+
const editing = noteEdit !== null && noteEdit.key === key;
|
|
4665
|
+
const busy = annotationBusy === key || annotationBusy === packageName;
|
|
4666
|
+
const draft = badgeDraft[key] ?? "";
|
|
4667
|
+
const undo = badgeUndo[key];
|
|
4668
|
+
const writeNote = (text) => annotationWrite({ action: "note", packageName, rowId: entry?.rowId ?? null, note: text }, text.trim() === "" ? t("noteCleared") : t("noteSaved"));
|
|
4669
|
+
const writeBadges = (list, okText) => annotationWrite({ action: "badges", packageName, rowId: entry?.rowId ?? null, badges: list }, okText);
|
|
4670
|
+
const addBadge = () => {
|
|
4671
|
+
const text = draft.trim();
|
|
4672
|
+
if (text === "") return;
|
|
4673
|
+
setBadgeDraft((prev) => ({ ...prev, [key]: "" }));
|
|
4674
|
+
if (badges.includes(text)) return;
|
|
4675
|
+
if (badges.length >= 12) { setAnnotationsNote(t("badgeMax").replace("{n}", "12")); return; }
|
|
4676
|
+
writeBadges([...badges, text], t("badgeAdded").replace("{b}", text));
|
|
4677
|
+
};
|
|
4678
|
+
return el("div", { className: styles.userNote, "data-annot": "true" },
|
|
4679
|
+
editing
|
|
4680
|
+
? el("div", null,
|
|
4681
|
+
el("textarea", {
|
|
4682
|
+
className: styles.noteInput,
|
|
4683
|
+
rows: 2,
|
|
4684
|
+
value: noteEdit.draft,
|
|
4685
|
+
placeholder: t("notePlaceholder"),
|
|
4686
|
+
"aria-label": t("noteLabel"),
|
|
4687
|
+
onChange: (event) => setNoteEdit({ key, draft: String(event.target.value ?? "") }),
|
|
4688
|
+
onKeyDown: (event) => {
|
|
4689
|
+
if (event.key === "Escape") { event.preventDefault(); setNoteEdit(null); return; }
|
|
4690
|
+
if (event.key === "Enter" && event.shiftKey !== true) {
|
|
4691
|
+
event.preventDefault();
|
|
4692
|
+
const text = noteEdit.draft;
|
|
4693
|
+
setNoteEdit(null);
|
|
4694
|
+
writeNote(text);
|
|
4695
|
+
}
|
|
4696
|
+
},
|
|
4697
|
+
}),
|
|
4698
|
+
el("p", { className: styles.note, style: { margin: 0 } }, t("noteSaveHint")))
|
|
4699
|
+
: el("div", { className: styles.annotRow },
|
|
4700
|
+
note !== "" ? el("p", { className: styles.desc, style: { margin: 0, whiteSpace: "pre-wrap", flex: "1 1 200px" } }, note) : null,
|
|
4701
|
+
el("button", {
|
|
4702
|
+
type: "button",
|
|
4703
|
+
className: styles.toggle,
|
|
4704
|
+
disabled: busy,
|
|
4705
|
+
title: t("notePlaceholder"),
|
|
4706
|
+
onClick: () => setNoteEdit({ key, draft: note }),
|
|
4707
|
+
}, note !== "" ? t("noteEdit") : t("noteEmpty"))),
|
|
4708
|
+
el("div", { className: styles.annotRow },
|
|
4709
|
+
el("span", { className: styles.status }, t("badgeLabel") + ":"),
|
|
4710
|
+
badges.map((badge) => el("span", { key: "ub-" + badge, className: styles.tag, "data-user-badge": "true" },
|
|
4711
|
+
badge,
|
|
4712
|
+
el("button", {
|
|
4713
|
+
type: "button",
|
|
4714
|
+
className: styles.tag,
|
|
4715
|
+
"data-more": "true",
|
|
4716
|
+
title: t("badgeRemove"),
|
|
4717
|
+
"aria-label": t("badgeRemove"),
|
|
4718
|
+
disabled: busy,
|
|
4719
|
+
onClick: () => {
|
|
4720
|
+
setBadgeUndo((prev) => ({ ...prev, [key]: badges }));
|
|
4721
|
+
writeBadges(badges.filter((item) => item !== badge), t("badgeRemoved").replace("{b}", badge));
|
|
4722
|
+
},
|
|
4723
|
+
}, "×"))),
|
|
4724
|
+
el("input", {
|
|
4725
|
+
type: "text",
|
|
4726
|
+
value: draft,
|
|
4727
|
+
placeholder: t("badgeAddPlaceholder"),
|
|
4728
|
+
"aria-label": t("badgeAddPlaceholder"),
|
|
4729
|
+
disabled: busy,
|
|
4730
|
+
style: { width: 130, font: "inherit", padding: "2px 6px" },
|
|
4731
|
+
onChange: (event) => setBadgeDraft((prev) => ({ ...prev, [key]: String(event.target.value ?? "") })),
|
|
4732
|
+
onKeyDown: (event) => { if (event.key === "Enter") { event.preventDefault(); addBadge(); } },
|
|
4733
|
+
}),
|
|
4734
|
+
undo !== undefined
|
|
4735
|
+
? el("button", {
|
|
4736
|
+
type: "button",
|
|
4737
|
+
className: styles.toggle,
|
|
4738
|
+
disabled: busy,
|
|
4739
|
+
onClick: () => {
|
|
4740
|
+
writeBadges(undo, t("badgeUndoDone"));
|
|
4741
|
+
setBadgeUndo((prev) => { const next = { ...prev }; delete next[key]; return next; });
|
|
4742
|
+
},
|
|
4743
|
+
}, t("badgeUndo"))
|
|
4744
|
+
: null),
|
|
4745
|
+
el("div", { className: styles.annotRow },
|
|
4746
|
+
el("span", { className: styles.status }, t("groupLabel") + ":"),
|
|
4747
|
+
// #5:就在这里新建分组,并把当前插件直接拉进去(回车创建)
|
|
4748
|
+
detailGroupDraft === null || detailGroupDraft.key !== key
|
|
4749
|
+
? el("button", {
|
|
4750
|
+
type: "button",
|
|
4751
|
+
className: styles.toggle,
|
|
4752
|
+
"data-newgroup": "true",
|
|
4753
|
+
title: t("groupNewInlineHint"),
|
|
4754
|
+
disabled: busy,
|
|
4755
|
+
onClick: () => setDetailGroupDraft({ key, draft: "" }),
|
|
4756
|
+
}, "+ " + t("groupNewInline"))
|
|
4757
|
+
: el("input", {
|
|
4758
|
+
type: "text",
|
|
4759
|
+
autoFocus: true,
|
|
4760
|
+
value: detailGroupDraft.draft,
|
|
4761
|
+
placeholder: t("groupNewInlinePlaceholder"),
|
|
4762
|
+
"aria-label": t("groupNewInline"),
|
|
4763
|
+
style: { font: "inherit", padding: "2px 6px", width: 160 },
|
|
4764
|
+
onChange: (event) => setDetailGroupDraft({ key, draft: String(event.target.value ?? "") }),
|
|
4765
|
+
onKeyDown: (event) => {
|
|
4766
|
+
if (event.key === "Escape") { event.preventDefault(); setDetailGroupDraft(null); return; }
|
|
4767
|
+
if (event.key !== "Enter") return;
|
|
4768
|
+
event.preventDefault();
|
|
4769
|
+
const name = String(detailGroupDraft.draft ?? "").trim();
|
|
4770
|
+
if (name === "") { setDetailGroupDraft(null); return; }
|
|
4771
|
+
setDetailGroupDraft(null);
|
|
4772
|
+
// 建组 → 用返回的全量 groups 找到它 → 把当前插件拉进去
|
|
4773
|
+
annotationWrite({ action: "group-create", name }, t("groupCreated").replace("{name}", name))
|
|
4774
|
+
.then((data) => {
|
|
4775
|
+
if (data === null) return;
|
|
4776
|
+
const created = (Array.isArray(data.groups) ? data.groups : [])
|
|
4777
|
+
.find((item) => String(item.name) === name);
|
|
4778
|
+
if (created !== undefined && created !== null) {
|
|
4779
|
+
annotationWrite({ action: "group-assign", id: created.id, packageName }, t("groupAssigned").replace("{name}", name));
|
|
4780
|
+
}
|
|
4781
|
+
});
|
|
4782
|
+
},
|
|
4783
|
+
}),
|
|
4784
|
+
el("select", {
|
|
4785
|
+
value: group?.id ?? "",
|
|
4786
|
+
"aria-label": t("groupLabel"),
|
|
4787
|
+
disabled: busy,
|
|
4788
|
+
style: { font: "inherit", padding: "2px 6px" },
|
|
4789
|
+
onChange: (event) => {
|
|
4790
|
+
const picked = String(event.target.value ?? "");
|
|
4791
|
+
if (picked === "") {
|
|
4792
|
+
if (group === null) return;
|
|
4793
|
+
annotationWrite({ action: "group-assign", id: group.id, packageName, member: false }, t("groupUnassigned"));
|
|
4794
|
+
return;
|
|
4795
|
+
}
|
|
4796
|
+
const pickedName = String(userGroups.find((item) => item.id === picked)?.name ?? "");
|
|
4797
|
+
annotationWrite({ action: "group-assign", id: picked, packageName }, t("groupAssigned").replace("{name}", pickedName));
|
|
4798
|
+
},
|
|
4799
|
+
},
|
|
4800
|
+
el("option", { value: "" }, t("groupNone")),
|
|
4801
|
+
userGroups.map((item) => el("option", { key: "gs-" + item.id, value: item.id }, String(item.name))))));
|
|
4802
|
+
};
|
|
4278
4803
|
const extraIds = new Set((state.status === "ready" ? state.data.patch?.inserts ?? [] : []));
|
|
4279
4804
|
const extraCount = state.status === "ready" ? state.data.entries.filter((entry) => entry.extra === true || extraIds.has(entry.rowId)).length : 0;
|
|
4805
|
+
// issue #22 ②③:打开面板即读一次注解(读失败只提示,插件列表照常)
|
|
4806
|
+
react.useEffect(() => { annotationsLoad(); }, []);
|
|
4280
4807
|
const pkgRootOf = (m) => { const mm = String(m ?? ""); const x = mm.match(/^(@[^/]+\/[^/]+)(?:\/.*)?$/u); return x ? x[1] : mm; };
|
|
4281
4808
|
const filteredEntries = state.status === "ready"
|
|
4282
4809
|
? state.data.entries.filter((entry) => {
|
|
4283
4810
|
if (extraOnly && !(entry.extra === true || extraIds.has(entry.rowId))) return false;
|
|
4811
|
+
// issue #22 ①:「仅看可更新」只筛**本次批量检测确实查到新版**的行。
|
|
4812
|
+
// 没查到结论的行(未检测到 / 没查过)**不算**可更新 —— 与"没查到 ≠ 已最新"同一条纪律。
|
|
4813
|
+
if (onlyUpdatable && batchItemOf(entry)?.status !== "update") return false;
|
|
4284
4814
|
if (installedQueryNorm === "") return true;
|
|
4285
|
-
|
|
4815
|
+
// ★ 用户 2026-10-27:搜索也要命中**备注**(备注是可用来命名的,只按包名搜会漏)
|
|
4816
|
+
const noteText = (() => {
|
|
4817
|
+
const key = annotationKeyOf(entry);
|
|
4818
|
+
if (key === "") return "";
|
|
4819
|
+
const rec = annotations !== null && annotations.plugins !== null && annotations.plugins !== undefined
|
|
4820
|
+
? annotations.plugins[key] ?? null
|
|
4821
|
+
: null;
|
|
4822
|
+
return typeof rec?.note === "string" ? rec.note : "";
|
|
4823
|
+
})();
|
|
4824
|
+
return [entry.moduleName, entry.entryId, entry.rowId, noteText]
|
|
4286
4825
|
.some((value) => String(value ?? "").toLowerCase().includes(installedQueryNorm));
|
|
4287
4826
|
})
|
|
4288
4827
|
: [];
|
|
@@ -4302,10 +4841,33 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4302
4841
|
familyCounts.set(root, set);
|
|
4303
4842
|
}
|
|
4304
4843
|
const familySize = (root) => (familyCounts.get(root)?.size ?? 0);
|
|
4305
|
-
const familyCards = filteredEntries.map((entry) => {
|
|
4844
|
+
const familyCards = filteredEntries.map((entry, cardIndex) => {
|
|
4306
4845
|
const familyRoot = String(entry.moduleName ?? "").startsWith("@deepseek-ai/") ? null : pkgRootOf(entry.moduleName);
|
|
4307
4846
|
void familyRoot;
|
|
4308
4847
|
const open = details !== null && details.entryId === entry.entryId;
|
|
4848
|
+
// issue #22 ①:这张卡本次的批量检测结论(null = 没查过/查中/查失败 ⇒ 不加任何"已最新"标签)
|
|
4849
|
+
const batchItem = batchItemOf(entry);
|
|
4850
|
+
// issue #22 ②:卡片徽章 = **系统徽章(现有顺序,一条不少)** + 用户徽章(追加在最后)。
|
|
4851
|
+
// 系统徽章用现有实心/描边样式(props 原样搬过来),用户徽章带 data-user-badge(虚线+浅底)。
|
|
4852
|
+
const systemBadges = [
|
|
4853
|
+
{ key: "enabled", label: t(entry.enabled ? "enabledTag" : "disabledTag"), props: { "data-enabled": entry.enabled ? "true" : "false" } },
|
|
4854
|
+
entry.extra === true ? { key: "extra", label: t("extraTag"), props: { "data-user": "true" } } : null,
|
|
4855
|
+
entry.protected ? { key: "protected", label: t("protectedTag"), props: { "data-user": "true" } } : null,
|
|
4856
|
+
entry.userDisabled ? { key: "userDisabled", label: t("userDisabledTag"), props: { "data-user": "true" } } : null,
|
|
4857
|
+
entry.userForced ? { key: "userForced", label: t("userForcedTag"), props: { "data-user": "true" } } : null,
|
|
4858
|
+
batchItem?.status === "update"
|
|
4859
|
+
? { key: "batch-update", label: t("batchUpdateAvailable").replace("{v}", String(batchItem.target ?? "?")), props: { "data-pending": "true", title: t("batchUpdateAvailableTip") } }
|
|
4860
|
+
: batchItem?.status === "current"
|
|
4861
|
+
? { key: "batch-current", label: t("batchUpToDate"), props: { "data-enabled": "true" } }
|
|
4862
|
+
: batchItem?.status === "unknown"
|
|
4863
|
+
? { key: "batch-unknown", label: t("batchUpdateUnknown"), props: { title: t("batchUpdateUnknownTip") + (batchItem.error ? "(" + String(batchItem.error) + ")" : "") } }
|
|
4864
|
+
: null,
|
|
4865
|
+
entry.disableSource === "bundle" ? { key: "bundle", label: t("disabledByBundleTag"), props: { "data-user": "true", "data-source": "bundle" } } : null,
|
|
4866
|
+
entry.forcedNotLoaded === true ? { key: "ineffective", label: t("forcedNotLoadedTag"), props: { "data-user": "true", "data-source": "ineffective" } } : null,
|
|
4867
|
+
].filter((badge) => badge !== null);
|
|
4868
|
+
const userBadges = (Array.isArray(annotationOf(entry)?.badges) ? annotationOf(entry).badges : [])
|
|
4869
|
+
.map((label) => ({ key: "user-" + label, label: String(label), props: { "data-user-badge": "true", title: t("badgeLabel") } }));
|
|
4870
|
+
const allBadges = [...systemBadges, ...userBadges];
|
|
4309
4871
|
let detailPanel = null;
|
|
4310
4872
|
if (open) {
|
|
4311
4873
|
if (details.status === "loading") {
|
|
@@ -4320,20 +4882,10 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4320
4882
|
const compatInfo = state.status === "ready" && state.data.compatPending
|
|
4321
4883
|
? (state.data.compatPending.pending ?? []).find((p) => p.rowId === entry.rowId) ?? null
|
|
4322
4884
|
: 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),
|
|
4885
|
+
// ★ issue #22 ②(用户定稿):动作按钮排在详情页**最后**(原来挂在"版本"那一行,夹在中间)。
|
|
4886
|
+
// 判据/禁用条件/文案逐字搬过来,一个字都没改,只换了位置。
|
|
4887
|
+
const detailActionRow = meta !== null && meta.version
|
|
4888
|
+
? el("div", { className: styles.rowTop },
|
|
4337
4889
|
el("button", {
|
|
4338
4890
|
type: "button",
|
|
4339
4891
|
className: styles.toggle,
|
|
@@ -4365,7 +4917,29 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4365
4917
|
);
|
|
4366
4918
|
},
|
|
4367
4919
|
}, adaptUnlocking === entry.entryId ? t("loadingPhase") : t("adaptUnlockBtn"))
|
|
4368
|
-
: null)
|
|
4920
|
+
: null)
|
|
4921
|
+
: null;
|
|
4922
|
+
detailPanel = el("div", { className: styles.detail },
|
|
4923
|
+
// ★ 0.5.93(issue #22 ② 收口):卡片上放不下的徽章**不提示、不展开**,
|
|
4924
|
+
// 全部徽章(系统徽章 + 用户徽章,顺序与卡片一致)在详情页里一次看全 ——
|
|
4925
|
+
// 这也是"卡片不再出现 +N"之后"看不到的徽章去哪了"的唯一答案。
|
|
4926
|
+
el("div", { className: styles.badgeAll, "data-allbadges": "true", title: t("badgesAll").replace("{n}", String(allBadges.length)) },
|
|
4927
|
+
allBadges.map((badge) => el("span", { key: "detail-" + badge.key, className: styles.tag, ...badge.props }, badge.label))),
|
|
4928
|
+
// ★ issue #22 ②(用户定稿的详情页顺序):用户备注/用户徽章放**最前面**,
|
|
4929
|
+
// 系统诊断 → 版本/仓库/说明摘要 → 动作按钮依次在其后。
|
|
4930
|
+
annotationBlock(entry),
|
|
4931
|
+
meta !== null ? el("p", { className: styles.desc }, meta.description ?? t("noDetail")) : null,
|
|
4932
|
+
entry.pendingCompat === true
|
|
4933
|
+
? el("p", { className: styles.status, "data-pending": entry.adoptable ? "false" : "true" },
|
|
4934
|
+
entry.adoptable
|
|
4935
|
+
? (entry.adoptable.basis === "verdict-rescinded"
|
|
4936
|
+
// 0.5.34:旧判据的误报已被当前判据撤回(真机 dshmarket 现场)——如实说是"判据更新",
|
|
4937
|
+
// 不能说成"插件已适配"(那会让人以为作者发过新版)
|
|
4938
|
+
? t("adoptableRescinded").replace("{version}", String(entry.adoptable.version ?? "?"))
|
|
4939
|
+
: t("adoptableDetected").replace("{version}", String(entry.adoptable.version ?? "?")).replace("{prev}", String(compatInfo?.version ?? "?")))
|
|
4940
|
+
: t("pendingCompatHint") + (compatInfo && compatInfo.checkNote ? ";已知校验:" + compatInfo.checkNote : ""))
|
|
4941
|
+
: null,
|
|
4942
|
+
meta !== null && meta.version ? el("p", { className: styles.status }, t("versionLabel") + ":" + meta.version) : null,
|
|
4369
4943
|
updateCheck !== null && updateCheck.status === "ready" && updateCheck.migrate && updateCheck.migrate.compatible
|
|
4370
4944
|
? el("div", null,
|
|
4371
4945
|
el("div", { className: styles.rowTop },
|
|
@@ -4418,28 +4992,34 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4418
4992
|
readme.summary ? el("p", { className: styles.desc }, readme.summary) : null,
|
|
4419
4993
|
)
|
|
4420
4994
|
: meta === null ? el("p", { className: styles.status }, t("noDetail")) : null,
|
|
4995
|
+
detailActionRow,
|
|
4421
4996
|
);
|
|
4422
4997
|
}
|
|
4423
4998
|
}
|
|
4424
4999
|
return el("li", { key: entry.entryId, className: styles.row },
|
|
4425
|
-
el("div", { className: styles.rowTop },
|
|
4426
|
-
el("strong", { className: styles.name, title: entry.moduleName }, (() => {
|
|
5000
|
+
el("div", { className: styles.rowTop, style: { minWidth: 0 } },
|
|
5001
|
+
el("strong", { className: styles.name, title: entry.moduleName, style: { minWidth: 0 } }, (() => {
|
|
4427
5002
|
const famRoot = famRootOf(entry);
|
|
4428
5003
|
const mn = String(entry.moduleName ?? "");
|
|
4429
|
-
|
|
5004
|
+
const shortName = famRoot !== null && familySize(famRoot) > 1 && mn.startsWith(famRoot + "/")
|
|
4430
5005
|
? mn.slice(famRoot.length + 1)
|
|
4431
5006
|
: moduleShortName(entry.moduleName);
|
|
5007
|
+
// ★ 用户 2026-10-26:备注可以**覆盖显示名**。有备注时标题用备注(强调色 + ✎ 标记),
|
|
5008
|
+
// 原短名降到旁边的小字 —— 一眼能看出"这个插件被我备注过",与没备注的区分开。
|
|
5009
|
+
const rec = annotationOf(entry);
|
|
5010
|
+
const custom = typeof rec?.note === "string" ? rec.note.trim() : "";
|
|
5011
|
+
if (custom === "") return shortName;
|
|
5012
|
+
return el("span", { "data-note-name": "true", style: { display: "inline-flex", alignItems: "baseline", gap: 6, minWidth: 0 } },
|
|
5013
|
+
el("span", { className: styles.noteName ?? undefined, style: { color: "var(--dsw-alias-state-business-primary)", fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, custom),
|
|
5014
|
+
el("span", { style: { opacity: 0.55, fontWeight: 400, fontSize: ".85em", whiteSpace: "nowrap" } }, shortName));
|
|
4432
5015
|
})()),
|
|
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,
|
|
5016
|
+
// ★ 0.5.93(issue #22 ② 收口):徽章区**单行不换行**,放不下的直接看不见
|
|
5017
|
+
// (纯 CSS:`.pc_badgeRow` 固定高度 + overflow:hidden + white-space:nowrap),
|
|
5018
|
+
// 卡片上**不给** "+N"、不给"还有几个"的任何提示,也不做宽度测量 ——
|
|
5019
|
+
// 行为与这个功能上线前一致;全部徽章去详情页看。
|
|
5020
|
+
// 顺序(用户定稿):**先系统徽章(现有顺序一个字没改)→ 再用户徽章(描边样式)**。
|
|
5021
|
+
el("div", { className: styles.badgeRow },
|
|
5022
|
+
allBadges.map((badge) => el("span", { key: badge.key, className: styles.tag, ...badge.props }, badge.label))),
|
|
4443
5023
|
),
|
|
4444
5024
|
el("div", { className: styles.meta },
|
|
4445
5025
|
el("span", { className: styles.phase, "data-phase": entry.fiberPhase ?? "unobserved" },
|
|
@@ -4475,6 +5055,20 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4475
5055
|
title: entry.pendingCompat === true ? (entry.compatRescinded === true ? t("compatRescindedHint") : t("pendingCompatHint")) : undefined,
|
|
4476
5056
|
onClick: () => toggle(entry, !entry.enabled),
|
|
4477
5057
|
}, busy === entry.entryId ? t("loadingPhase") : (entry.pendingCompat === true ? (entry.compatRescinded === true ? t("compatRescindedTag") : t("pendingCompatTag")) : t(entry.enabled ? "off" : "on"))),
|
|
5058
|
+
// ★ issue #22 ①:批量检测到新版的卡片直接给「更新」按钮 —— 与详情卡片里那个按钮
|
|
5059
|
+
// **完全同一条路**(startJob(-, moduleName, github, plugin, update=true, -, target)),
|
|
5060
|
+
// 目标是服务端唯一判据算出的 target;点下去走的是既有安装/更新作业,不新增任何写盘路径。
|
|
5061
|
+
batchItem !== null && batchItem.status === "update"
|
|
5062
|
+
? el("button", {
|
|
5063
|
+
type: "button",
|
|
5064
|
+
className: styles.toggle,
|
|
5065
|
+
title: t("batchUpdateAvailableTip"),
|
|
5066
|
+
disabled: installing === entry.moduleName || inFlight.has(entry.moduleName),
|
|
5067
|
+
onClick: () => startJob("", entry.moduleName, "github", "plugin", true, null, batchItem.target ?? null),
|
|
5068
|
+
}, installing === entry.moduleName
|
|
5069
|
+
? t("installing")
|
|
5070
|
+
: (entry.pendingCompat === true ? t("updateAndAdapt") : t("update") + " → v" + String(batchItem.target ?? "?")))
|
|
5071
|
+
: null,
|
|
4478
5072
|
// ★ 0.5.76:补丁写了强制开启但运行时没起来 ⇒ 给一个撤销覆盖的入口
|
|
4479
5073
|
entry.userForced === true ? el("button", {
|
|
4480
5074
|
type: "button",
|
|
@@ -4602,11 +5196,26 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4602
5196
|
// 组头放到最底部:普通条目在前,全家桶(组头+子包卡片)整体在后
|
|
4603
5197
|
const nonFamilyCards = [];
|
|
4604
5198
|
const familySections = new Map();
|
|
5199
|
+
// issue #22 ③:用户自建分组 → 折叠区块。**官方 family 优先**(用户拍定的规则):
|
|
5200
|
+
// 属于官方 family 的插件即使被归进用户分组,也**留在官方 family 区块里**,不进用户分组区块 ——
|
|
5201
|
+
// 官方 family 的渲染因此一个字都没变,两者共存且不会出现同一插件两处重复。
|
|
5202
|
+
const userGroupBuckets = new Map();
|
|
4605
5203
|
familyCards.forEach((card, i) => {
|
|
4606
5204
|
const e = filteredEntries[i];
|
|
4607
5205
|
if (e === undefined) { nonFamilyCards.push(card); return; }
|
|
4608
5206
|
const root = famRootOf(e);
|
|
4609
|
-
|
|
5207
|
+
// ★ 用户 2026-10-26:**用户分组优先** —— 被归进自己分组的插件,卡片直接住进那个分组区块
|
|
5208
|
+
// (像全家桶一样能展开看到卡片),官方 family 区块里不再重复渲染它。
|
|
5209
|
+
const userGroup = groupOfPackage(e.moduleName);
|
|
5210
|
+
if (userGroup !== null) {
|
|
5211
|
+
if (!userGroupBuckets.has(userGroup.id)) userGroupBuckets.set(userGroup.id, []);
|
|
5212
|
+
userGroupBuckets.get(userGroup.id).push(card);
|
|
5213
|
+
return;
|
|
5214
|
+
}
|
|
5215
|
+
if (root === null || familySize(root) <= 1) {
|
|
5216
|
+
nonFamilyCards.push(card);
|
|
5217
|
+
return;
|
|
5218
|
+
}
|
|
4610
5219
|
if (!familySections.has(root)) familySections.set(root, { head: familyHeaders.find((h) => h && h.key === "family-" + root) ?? null, cards: [] });
|
|
4611
5220
|
familySections.get(root).cards.push(card);
|
|
4612
5221
|
});
|
|
@@ -4671,7 +5280,131 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
4671
5280
|
),
|
|
4672
5281
|
),
|
|
4673
5282
|
));
|
|
4674
|
-
|
|
5283
|
+
// issue #22 ③(0.5.93 收口,用户定稿):用户自建分组在主列表里是**横框**,外观与官方 family
|
|
5284
|
+
// 区块**同一套视觉语言**(标题行 + 折叠箭头 + 容器 + 成员数徽章)。三条行为:
|
|
5285
|
+
// · 空分组(刚建出来、还没放插件)**也渲染** —— 否则"新建分组"在列表里根本看不到,
|
|
5286
|
+
// 等于点了没反应;
|
|
5287
|
+
// · 框里可以直接**加入 / 移出**插件(走 group-assign;服务端保证一个包只属于一个分组,
|
|
5288
|
+
// 移出只解除归属,不删插件、不动备注与徽章);
|
|
5289
|
+
// · **只含官方 family 成员**的分组仍不画(官方 family 优先:那些成员留在 family 区块里,
|
|
5290
|
+
// 绝不出现同一插件两处重复);
|
|
5291
|
+
// · 刚建出来的那个横框多挂一个 pc_groupNew(220ms 入场动画;prefers-reduced-motion 下不播)。
|
|
5292
|
+
// 数据来源只有服务端那一份 groups(annotationWrite 用响应里的全量状态覆盖 annotations),
|
|
5293
|
+
// 客户端额外只记一个"刚建好的 id"用于播动画,不维护第二份状态。
|
|
5294
|
+
const userGroupFrame = (group, bucket) => {
|
|
5295
|
+
const members = (Array.isArray(group.packages) ? group.packages : []).map((item) => String(item));
|
|
5296
|
+
const inGroup = new Set(members);
|
|
5297
|
+
const candidates = [];
|
|
5298
|
+
for (const entry of (state.status === "ready" ? state.data.entries : [])) {
|
|
5299
|
+
const name = String(entry.moduleName ?? "");
|
|
5300
|
+
if (name === "" || inGroup.has(name) || candidates.includes(name)) continue;
|
|
5301
|
+
candidates.push(name);
|
|
5302
|
+
}
|
|
5303
|
+
const fresh = groupJustCreated !== null && String(group.id) === String(groupJustCreated);
|
|
5304
|
+
const header = el("div", { className: styles.rowTop, style: { alignItems: "center" } },
|
|
5305
|
+
el("strong", { className: styles.name, style: { flex: "0 0 auto", whiteSpace: "nowrap" }, title: String(group.name) }, String(group.name)),
|
|
5306
|
+
el("span", { className: styles.tag, "data-user-badge": "true" }, t("userGroupTag") + " · " + members.length + " " + t("familySubs")),
|
|
5307
|
+
el("button", {
|
|
5308
|
+
type: "button",
|
|
5309
|
+
className: styles.toggle,
|
|
5310
|
+
style: { whiteSpace: "nowrap", flexShrink: 0 },
|
|
5311
|
+
disabled: annotationBusy === group.id,
|
|
5312
|
+
onClick: () => annotationWrite({ action: "group-collapse", id: group.id, collapsed: group.collapsed !== true }, null),
|
|
5313
|
+
}, group.collapsed === true ? "▸ " + t("familyExpand") : "▾ " + t("familyCollapse")),
|
|
5314
|
+
// ★ 用户 2026-10-27:分组框也要有「批量检测更新 / 一键启用已适配 / 删除分组」
|
|
5315
|
+
el("button", {
|
|
5316
|
+
type: "button",
|
|
5317
|
+
className: styles.toggle,
|
|
5318
|
+
style: { whiteSpace: "nowrap", flexShrink: 0 },
|
|
5319
|
+
disabled: annotationBusy === group.id,
|
|
5320
|
+
onClick: () => {
|
|
5321
|
+
// 批检:组内每个成员各查一次(单包接口复用,判据不分叉)
|
|
5322
|
+
setMessage(t("checkingUpdate"));
|
|
5323
|
+
const names = members.slice();
|
|
5324
|
+
Promise.all(names.map((nm) => call("/plugin-console/check-update", { packageName: nm }).catch(() => null)))
|
|
5325
|
+
.then((list) => {
|
|
5326
|
+
const hits = list.filter((d) => d !== null && typeof d?.target === "string" && d.target !== "")
|
|
5327
|
+
setMessage(hits.length > 0 ? t("familyUpdate") + ":" + hits.length + " / " + names.length : t("upToDate"));
|
|
5328
|
+
});
|
|
5329
|
+
},
|
|
5330
|
+
}, t("familyCheckUpdate")),
|
|
5331
|
+
el("button", {
|
|
5332
|
+
type: "button",
|
|
5333
|
+
className: styles.toggle,
|
|
5334
|
+
style: { whiteSpace: "nowrap", flexShrink: 0 },
|
|
5335
|
+
disabled: annotationBusy === group.id,
|
|
5336
|
+
onClick: () => {
|
|
5337
|
+
// 一键启用已适配:只对组内有 adoptable 的成员解锁
|
|
5338
|
+
const ready = bucket
|
|
5339
|
+
.map((card) => card?.key ?? null)
|
|
5340
|
+
.filter((x) => x !== null);
|
|
5341
|
+
const targets = (state.status === "ready" ? state.data.entries : [])
|
|
5342
|
+
.filter((e) => members.includes(String(e.moduleName ?? "")) && e.adoptable !== null && e.adoptable !== undefined);
|
|
5343
|
+
if (targets.length === 0) { setMessage(t("gateNoAdoptable")); return; }
|
|
5344
|
+
Promise.all(targets.map((e) => call("/plugin-console/adapt-unlock", { entryId: e.entryId }).catch(() => null)))
|
|
5345
|
+
.then(() => { setMessage(t("familyUnlockAll") + ":" + targets.length); refresh(); });
|
|
5346
|
+
},
|
|
5347
|
+
}, t("familyUnlockAll")),
|
|
5348
|
+
el("button", {
|
|
5349
|
+
type: "button",
|
|
5350
|
+
className: styles.toggle,
|
|
5351
|
+
style: { display: "none" },
|
|
5352
|
+
title: t("annotationsPanelTitle"),
|
|
5353
|
+
onClick: () => { openAnnotationsPanel(); },
|
|
5354
|
+
}, t("annotationsBtn")));
|
|
5355
|
+
const memberChips = members.length === 0
|
|
5356
|
+
? el("span", { className: styles.status }, t("groupNoMembers"))
|
|
5357
|
+
: members.map((name) => el("span", { key: "ugm-" + name, className: styles.tag, "data-usergroup-member": "true", title: name }, name,
|
|
5358
|
+
el("button", {
|
|
5359
|
+
type: "button",
|
|
5360
|
+
className: styles.tag,
|
|
5361
|
+
"data-more": "true",
|
|
5362
|
+
title: t("groupMemberRemove"),
|
|
5363
|
+
"aria-label": t("groupMemberRemove"),
|
|
5364
|
+
disabled: annotationBusy === group.id,
|
|
5365
|
+
onClick: () => annotationWrite({ action: "group-assign", id: group.id, packageName: name, member: false }, t("groupUnassigned")),
|
|
5366
|
+
}, "×")));
|
|
5367
|
+
const addSelect = el("select", {
|
|
5368
|
+
value: "",
|
|
5369
|
+
"aria-label": "groupMemberAdd",
|
|
5370
|
+
disabled: annotationBusy === group.id || candidates.length === 0,
|
|
5371
|
+
style: { font: "inherit", padding: "2px 6px", maxWidth: 320 },
|
|
5372
|
+
onChange: (event) => {
|
|
5373
|
+
const picked = String(event.target.value ?? "");
|
|
5374
|
+
if (picked === "") return;
|
|
5375
|
+
annotationWrite({ action: "group-assign", id: group.id, packageName: picked }, t("groupAssigned").replace("{name}", String(group.name)));
|
|
5376
|
+
},
|
|
5377
|
+
},
|
|
5378
|
+
el("option", { value: "" }, candidates.length === 0 ? t("groupMemberNone") : t("groupMemberPick")),
|
|
5379
|
+
candidates.map((name) => el("option", { key: "ugc-" + name, value: name }, name)));
|
|
5380
|
+
const body = el("div", { style: { flexDirection: "column", gap: 6, display: "flex", marginTop: 2 } },
|
|
5381
|
+
el("div", { className: styles.annotRow }, memberChips, addSelect),
|
|
5382
|
+
bucket.length > 0
|
|
5383
|
+
? el("ul", { className: styles.list }, bucket)
|
|
5384
|
+
: members.length > 0
|
|
5385
|
+
? el("p", { className: styles.status, "data-in-family": "true" }, t("groupInFamilyNote"))
|
|
5386
|
+
: null);
|
|
5387
|
+
return el("div", {
|
|
5388
|
+
key: "usergroup-" + group.id,
|
|
5389
|
+
className: styles.detail + (fresh ? " " + styles.groupNew : ""),
|
|
5390
|
+
"data-usergroup": "true",
|
|
5391
|
+
"data-group-id": String(group.id),
|
|
5392
|
+
"data-new-group": fresh ? "true" : undefined,
|
|
5393
|
+
style: { border: "1px solid var(--dsw-alias-state-business-primary)", borderRadius: 10, padding: "8px 12px", gridColumn: "1 / -1" },
|
|
5394
|
+
},
|
|
5395
|
+
header,
|
|
5396
|
+
group.collapsed === true ? null : body);
|
|
5397
|
+
};
|
|
5398
|
+
const userGroupSectionEls = [];
|
|
5399
|
+
for (const group of userGroups) {
|
|
5400
|
+
const bucket = userGroupBuckets.get(group.id) ?? [];
|
|
5401
|
+
const members = Array.isArray(group.packages) ? group.packages : [];
|
|
5402
|
+
// ★ 用户 2026-10-26:分组框**永远画出来**。此前"成员全在官方 family 里就跳过"会让用户
|
|
5403
|
+
// 建完分组后在主列表什么都看不到(既没框也没入场动画),观感等于"点了没反应"。
|
|
5404
|
+
// 同一插件仍不重复渲染:那些成员留在官方 family 区块里,这里只给一句如实说明。
|
|
5405
|
+
userGroupSectionEls.push(userGroupFrame(group, bucket));
|
|
5406
|
+
}
|
|
5407
|
+
const rows = state.status === "ready" ? [...nonFamilyCards, ...familySectionEls, ...userGroupSectionEls, ...pendingCards] : [];
|
|
4675
5408
|
const marketShown = market === null || market.status !== "ready"
|
|
4676
5409
|
? []
|
|
4677
5410
|
: officialOnly
|
|
@@ -5225,7 +5958,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5225
5958
|
"aria-label": t("linksDevRootLabel"),
|
|
5226
5959
|
value: "",
|
|
5227
5960
|
disabled: linksPinBusy,
|
|
5228
|
-
onChange: (event) => { const picked = String(event.
|
|
5961
|
+
onChange: (event) => { const picked = String(event.target.value ?? ""); if (picked !== "") setLinksDir(devRootDraft(picked)); },
|
|
5229
5962
|
style: { flex: "0 1 300px", minWidth: 180, font: "inherit", padding: "4px 6px" },
|
|
5230
5963
|
},
|
|
5231
5964
|
el("option", { value: "" }, t("linksDevRootPick")),
|
|
@@ -5237,7 +5970,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5237
5970
|
value: linksDir,
|
|
5238
5971
|
disabled: linksPinBusy,
|
|
5239
5972
|
"aria-label": t("linksDirPlaceholder"),
|
|
5240
|
-
onChange: (event) => setLinksDir(String(event.
|
|
5973
|
+
onChange: (event) => setLinksDir(String(event.target.value ?? "")),
|
|
5241
5974
|
style: { flex: 1, minWidth: 220, font: "inherit", padding: "4px 8px" },
|
|
5242
5975
|
}),
|
|
5243
5976
|
// ★ 只在宿主**确实**有系统目录对话框时才渲染(web 端没有 ⇒ 不显示,不是灰掉)
|
|
@@ -5318,6 +6051,231 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5318
6051
|
el("div", { className: styles.rowTop, style: { justifyContent: "flex-end", gap: 8, marginTop: 10 } },
|
|
5319
6052
|
el("button", { type: "button", className: styles.toolItem, "data-busy": linksPanelBusy ? "true" : "false", onClick: () => { linksPanelLoad(); linksDevRootsLoad(); } }, linksPanelBusy ? t("linksPanelLoading") : t("linksPanelReload")))))
|
|
5320
6053
|
: null,
|
|
6054
|
+
annotationsPanelOpen
|
|
6055
|
+
? el("div", { className: styles.modalBackdrop, onClick: (event) => { if (event.target === event.currentTarget) setAnnotationsPanelOpen(false); } },
|
|
6056
|
+
el("div", { className: styles.modalCard, style: { maxWidth: 760, maxHeight: "82vh", overflow: "auto" } },
|
|
6057
|
+
el("div", { className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
6058
|
+
el("strong", { className: styles.name }, t("annotationsBtn")),
|
|
6059
|
+
el("button", { type: "button", className: styles.trashBtn, style: { fontSize: 14, padding: "4px 8px" }, title: t("closeModal"), "aria-label": t("closeModal"), onClick: () => setAnnotationsPanelOpen(false) }, "✕")),
|
|
6060
|
+
el("p", { className: styles.message }, t("annotationsPanelTitle")),
|
|
6061
|
+
annotations !== null && annotations.file !== null && annotations.file !== undefined
|
|
6062
|
+
? el("p", { className: styles.status }, String(annotations.file))
|
|
6063
|
+
: null,
|
|
6064
|
+
// 失败/降级一律有文字(绝不"点了一下没反应")
|
|
6065
|
+
annotationsNote !== null
|
|
6066
|
+
? el("p", { className: styles.message, "data-error": "true" }, annotationsNote)
|
|
6067
|
+
: null,
|
|
6068
|
+
// ── 分组管理:新建 / 重命名 / 删除(两段式)/ 撤销删除 ─────────────────
|
|
6069
|
+
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))" } },
|
|
6070
|
+
el("div", { className: styles.annotRow },
|
|
6071
|
+
el("input", {
|
|
6072
|
+
type: "text",
|
|
6073
|
+
value: groupDraftName,
|
|
6074
|
+
placeholder: t("groupCreatePlaceholder"),
|
|
6075
|
+
"aria-label": t("groupCreatePlaceholder"),
|
|
6076
|
+
disabled: annotationBusy !== null,
|
|
6077
|
+
style: { flex: 1, minWidth: 200, font: "inherit", padding: "4px 8px" },
|
|
6078
|
+
onChange: (event) => setGroupDraftName(String(event.target.value ?? "")),
|
|
6079
|
+
onKeyDown: (event) => { if (event.key === "Enter") { event.preventDefault(); createUserGroup(); } },
|
|
6080
|
+
}),
|
|
6081
|
+
el("button", { type: "button", className: styles.toggle, disabled: groupDraftName.trim() === "" || annotationBusy !== null, onClick: createUserGroup }, t("groupCreate"))),
|
|
6082
|
+
groupUndo !== null
|
|
6083
|
+
? el("div", { className: styles.annotRow },
|
|
6084
|
+
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))),
|
|
6085
|
+
el("button", {
|
|
6086
|
+
type: "button",
|
|
6087
|
+
className: styles.toggle,
|
|
6088
|
+
disabled: annotationBusy !== null,
|
|
6089
|
+
onClick: () => {
|
|
6090
|
+
const record = groupUndo;
|
|
6091
|
+
setGroupUndo(null);
|
|
6092
|
+
annotationWrite({ action: "group-restore", group: record.group, index: record.index }, t("groupRestored").replace("{name}", String(record.group?.name ?? "")));
|
|
6093
|
+
},
|
|
6094
|
+
}, t("groupRestore")))
|
|
6095
|
+
: null,
|
|
6096
|
+
userGroups.length === 0
|
|
6097
|
+
? el("p", { className: styles.status }, t("groupEmptyHint"))
|
|
6098
|
+
: el("ul", { className: styles.list }, userGroups.map((group) => el("li", { key: "ug-" + group.id, className: styles.row },
|
|
6099
|
+
el("div", { className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
6100
|
+
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)),
|
|
6101
|
+
el("span", { className: styles.tag, "data-user-badge": "true" }, t("groupMembers").replace("{n}", String(Array.isArray(group.packages) ? group.packages.length : 0)))),
|
|
6102
|
+
el("div", { className: styles.annotRow },
|
|
6103
|
+
groupRenameEdit !== null && groupRenameEdit.id === group.id
|
|
6104
|
+
? el("div", { className: styles.annotRow },
|
|
6105
|
+
el("input", {
|
|
6106
|
+
type: "text",
|
|
6107
|
+
value: groupRenameEdit.draft,
|
|
6108
|
+
"aria-label": t("groupRename"),
|
|
6109
|
+
style: { font: "inherit", padding: "2px 6px" },
|
|
6110
|
+
onChange: (event) => setGroupRenameEdit({ id: group.id, draft: String(event.target.value ?? "") }),
|
|
6111
|
+
onKeyDown: (event) => {
|
|
6112
|
+
if (event.key === "Escape") { event.preventDefault(); setGroupRenameEdit(null); return; }
|
|
6113
|
+
if (event.key === "Enter") {
|
|
6114
|
+
event.preventDefault();
|
|
6115
|
+
const draft = groupRenameEdit.draft;
|
|
6116
|
+
setGroupRenameEdit(null);
|
|
6117
|
+
annotationWrite({ action: "group-rename", id: group.id, name: draft }, t("groupRenamed"));
|
|
6118
|
+
}
|
|
6119
|
+
},
|
|
6120
|
+
}),
|
|
6121
|
+
el("button", { type: "button", className: styles.toggle, onClick: () => setGroupRenameEdit(null) }, t("linksCancel")))
|
|
6122
|
+
: el("button", { type: "button", className: styles.toggle, disabled: annotationBusy === group.id, onClick: () => setGroupRenameEdit({ id: group.id, draft: String(group.name) }) }, t("groupRename")),
|
|
6123
|
+
groupDeleteArm === group.id
|
|
6124
|
+
? el("div", { className: styles.annotRow },
|
|
6125
|
+
el("span", { className: styles.status, "data-pending": "true" }, t("groupDeleteConfirm").replace("{name}", String(group.name))),
|
|
6126
|
+
el("button", { type: "button", className: styles.toggle, onClick: () => setGroupDeleteArm(null) }, t("linksCancel")),
|
|
6127
|
+
el("button", {
|
|
6128
|
+
type: "button",
|
|
6129
|
+
className: styles.toggle,
|
|
6130
|
+
disabled: annotationBusy === group.id,
|
|
6131
|
+
onClick: () => {
|
|
6132
|
+
setGroupDeleteArm(null);
|
|
6133
|
+
const memberCount = Array.isArray(group.packages) ? group.packages.length : 0;
|
|
6134
|
+
// 两段式:第一次点只是"上膛",这里才是真删(且**只解除归属**)
|
|
6135
|
+
annotationWrite({ action: "group-delete", id: group.id }, t("groupDeleted").replace("{name}", String(group.name)).replace("{n}", String(memberCount))).then((data) => {
|
|
6136
|
+
const removed = data?.deleted;
|
|
6137
|
+
if (removed !== null && removed !== undefined && removed.group !== null && removed.group !== undefined) setGroupUndo({ group: removed.group, index: Number.isFinite(removed.index) ? removed.index : null });
|
|
6138
|
+
});
|
|
6139
|
+
},
|
|
6140
|
+
}, t("confirmDelete")))
|
|
6141
|
+
: el("button", { type: "button", className: styles.trashBtn, title: t("groupRemove"), "aria-label": t("groupRemove"), disabled: annotationBusy === group.id, onClick: () => setGroupDeleteArm(group.id) }, "✕")))))),
|
|
6142
|
+
// ★ 2026-10-27:点分组名 → 展开「成员 + 一键更新 + 搜索添加(含备注)」
|
|
6143
|
+
(() => {
|
|
6144
|
+
const open = userGroups.find((g) => g.id === panelGroupOpen) ?? null;
|
|
6145
|
+
if (open === null) {
|
|
6146
|
+
return el("p", { className: styles.status, "data-panel-groupadd-hint": "true" }, t("groupOpenHint"));
|
|
6147
|
+
}
|
|
6148
|
+
const inGroup = new Set((Array.isArray(open.packages) ? open.packages : []).map((x) => String(x)));
|
|
6149
|
+
const noteOf = (nm) => {
|
|
6150
|
+
const rec = annotations !== null && annotations.plugins !== null && annotations.plugins !== undefined
|
|
6151
|
+
? annotations.plugins[nm] ?? null
|
|
6152
|
+
: null;
|
|
6153
|
+
return typeof rec?.note === "string" ? rec.note : "";
|
|
6154
|
+
};
|
|
6155
|
+
const q = panelGroupQuery.trim().toLowerCase();
|
|
6156
|
+
const pool = (state.status === "ready" ? state.data.entries : [])
|
|
6157
|
+
.map((e) => String(e.moduleName ?? ""))
|
|
6158
|
+
.filter((nm) => nm !== "" && !nm.startsWith("cordis:") && !inGroup.has(nm))
|
|
6159
|
+
// ★ 用户 2026-10-27:搜索要能命中**备注**(不只是包名)
|
|
6160
|
+
.filter((nm) => q === "" || nm.toLowerCase().includes(q) || noteOf(nm).toLowerCase().includes(q));
|
|
6161
|
+
const shown = pool.slice(0, panelGroupShow);
|
|
6162
|
+
const hidden = Math.max(0, pool.length - shown.length);
|
|
6163
|
+
const chips = [...inGroup].map((nm) => el("span", { key: "pgm-" + nm, className: styles.tag, "data-usergroup-member": "true" },
|
|
6164
|
+
nm,
|
|
6165
|
+
el("button", {
|
|
6166
|
+
type: "button",
|
|
6167
|
+
className: styles.tag,
|
|
6168
|
+
"data-more": "true",
|
|
6169
|
+
title: t("groupMemberRemove"),
|
|
6170
|
+
onClick: () => annotationWrite({ action: "group-assign", id: open.id, packageName: nm, member: false }, t("groupUnassigned")),
|
|
6171
|
+
}, "×")))
|
|
6172
|
+
const headerRow = el("div", { className: styles.annotRow },
|
|
6173
|
+
el("strong", { className: styles.name }, String(open.name)),
|
|
6174
|
+
el("span", { className: styles.status }, t("groupMembers").replace("{n}", String(inGroup.size))),
|
|
6175
|
+
el("button", {
|
|
6176
|
+
type: "button",
|
|
6177
|
+
className: styles.toggle,
|
|
6178
|
+
"data-group-update-all": String(open.id),
|
|
6179
|
+
title: t("groupUpdateAllTip"),
|
|
6180
|
+
onClick: () => {
|
|
6181
|
+
const members2 = [...inGroup];
|
|
6182
|
+
if (members2.length === 0) { setMessage(t("groupNoMembers")); return; }
|
|
6183
|
+
// 逐个串行:先查目标版本,再按那个版本更新(与单卡同一判据),门控照旧生效
|
|
6184
|
+
const step = (i, hit) => {
|
|
6185
|
+
if (i >= members2.length) {
|
|
6186
|
+
setMessage(t("groupUpdateAllDone").replace("{hit}", String(hit)).replace("{n}", String(members2.length)));
|
|
6187
|
+
return;
|
|
6188
|
+
}
|
|
6189
|
+
const nm = members2[i];
|
|
6190
|
+
setMessage(t("groupUpdateAllProgress").replace("{i}", String(i + 1)).replace("{n}", String(members2.length)) + " — " + nm);
|
|
6191
|
+
call("/plugin-console/check-update", { packageName: nm }).then((d) => {
|
|
6192
|
+
const target = typeof d?.target === "string" && d.target !== "" ? d.target : (typeof d?.latest === "string" ? d.latest : null);
|
|
6193
|
+
if (target !== null) startJob("", nm, "github", "plugin", true, null, target);
|
|
6194
|
+
step(i + 1, target !== null ? hit + 1 : hit);
|
|
6195
|
+
}, () => step(i + 1, hit));
|
|
6196
|
+
};
|
|
6197
|
+
step(0, 0);
|
|
6198
|
+
},
|
|
6199
|
+
}, t("groupUpdateAll")))
|
|
6200
|
+
const searchInput = el("input", {
|
|
6201
|
+
type: "search",
|
|
6202
|
+
value: panelGroupQuery,
|
|
6203
|
+
placeholder: t("groupAddSearchPlaceholder"),
|
|
6204
|
+
"aria-label": t("groupAddSearchPlaceholder"),
|
|
6205
|
+
style: { font: "inherit", padding: "4px 8px", width: "100%" },
|
|
6206
|
+
onChange: (event) => setPanelGroupQuery(String(event.target.value ?? "")),
|
|
6207
|
+
})
|
|
6208
|
+
const candRows = shown.map((nm) => {
|
|
6209
|
+
const note = noteOf(nm);
|
|
6210
|
+
return el("li", { key: "pgc-" + nm, className: styles.row },
|
|
6211
|
+
el("div", { className: styles.rowTop },
|
|
6212
|
+
el("strong", { className: styles.name, title: nm, style: { minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, nm),
|
|
6213
|
+
note !== ""
|
|
6214
|
+
? el("span", { style: { color: "var(--dsw-alias-state-business-primary)", fontWeight: 500, fontSize: ".9em", whiteSpace: "nowrap", maxWidth: "45%", overflow: "hidden", textOverflow: "ellipsis" } }, note)
|
|
6215
|
+
: null,
|
|
6216
|
+
el("button", {
|
|
6217
|
+
type: "button",
|
|
6218
|
+
className: styles.toggle,
|
|
6219
|
+
"data-group-add": nm,
|
|
6220
|
+
disabled: annotationBusy !== null,
|
|
6221
|
+
onClick: () => annotationWrite({ action: "group-assign", id: open.id, packageName: nm }, t("groupAssigned").replace("{name}", String(open.name))),
|
|
6222
|
+
}, "+ " + t("groupAddOne"))));
|
|
6223
|
+
})
|
|
6224
|
+
const candList = el("ul", { className: styles.list, "data-group-candidates": "true" },
|
|
6225
|
+
pool.length === 0
|
|
6226
|
+
? el("li", { className: styles.row }, el("span", { className: styles.status }, t("groupAddNoMatch")))
|
|
6227
|
+
: candRows)
|
|
6228
|
+
const moreBtn = hidden > 0
|
|
6229
|
+
? el("button", { type: "button", className: styles.toggle, "data-group-loadmore": "true", onClick: () => setPanelGroupShow((prev) => prev + 20) }, t("groupLoadMore").replace("{n}", String(hidden)))
|
|
6230
|
+
: null
|
|
6231
|
+
return el("div", { "data-panel-groupadd": String(open.id), style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 6 } },
|
|
6232
|
+
headerRow,
|
|
6233
|
+
el("div", { className: styles.annotRow }, chips.length > 0 ? chips : el("span", { className: styles.status }, t("groupNoMembers"))),
|
|
6234
|
+
searchInput,
|
|
6235
|
+
candList,
|
|
6236
|
+
moreBtn)
|
|
6237
|
+
})(),
|
|
6238
|
+
// ── 插件行:备注 / 徽章 / 分组(每行一套,与详情页同一个编辑块)───────────
|
|
6239
|
+
el("p", { className: styles.message, style: { marginTop: 8 } },
|
|
6240
|
+
annotations === null
|
|
6241
|
+
? t("annotationsEmpty")
|
|
6242
|
+
: t("annotationsStats")
|
|
6243
|
+
.replace("{p}", String(annotations.stats?.packages ?? 0))
|
|
6244
|
+
.replace("{b}", String(annotations.stats?.badges ?? 0))
|
|
6245
|
+
.replace("{g}", String(annotations.stats?.groups ?? 0))),
|
|
6246
|
+
// ★ 0.5.93(issue #22 ②,用户定稿):列表**默认收起**,只留一行摘要「插件列表(N)· 点此展开」;
|
|
6247
|
+
// 点开才渲染搜索框与插件行。查询词是受控 state ⇒ 边打边筛,且过滤不会清掉输入框里的字。
|
|
6248
|
+
el("button", {
|
|
6249
|
+
type: "button",
|
|
6250
|
+
className: styles.toggle,
|
|
6251
|
+
"aria-label": "annotationsListToggle",
|
|
6252
|
+
"aria-expanded": annotationsListOpen ? "true" : "false",
|
|
6253
|
+
"data-open": annotationsListOpen ? "true" : "false",
|
|
6254
|
+
onClick: () => setAnnotationsListOpen((prev) => !prev),
|
|
6255
|
+
}, (annotationsListOpen ? t("annotationsListHide") : t("annotationsListShow"))
|
|
6256
|
+
.replace("{n}", String(annotationListEntries.length))),
|
|
6257
|
+
annotationsListOpen === false
|
|
6258
|
+
? null
|
|
6259
|
+
: el("div", { style: { flexDirection: "column", gap: 8, display: "flex", marginTop: 6 } },
|
|
6260
|
+
el("input", {
|
|
6261
|
+
type: "text",
|
|
6262
|
+
value: annotationsQuery,
|
|
6263
|
+
placeholder: t("annotationsSearchPlaceholder"),
|
|
6264
|
+
"aria-label": "annotationsSearch",
|
|
6265
|
+
style: { font: "inherit", padding: "4px 8px", borderRadius: 6 },
|
|
6266
|
+
onChange: (event) => setAnnotationsQuery(String(event.target.value ?? "")),
|
|
6267
|
+
}),
|
|
6268
|
+
annotationListEntries.length === 0
|
|
6269
|
+
? el("p", { className: styles.status }, t("empty"))
|
|
6270
|
+
: annotationListFiltered.length === 0
|
|
6271
|
+
? el("p", { className: styles.status, "data-nomatch": "true" }, t("annotationsNoMatch"))
|
|
6272
|
+
: el("ul", { className: styles.list, "data-annotlist": "true" }, annotationListFiltered.map((entry) => el("li", { key: "an-" + entry.entryId, className: styles.row },
|
|
6273
|
+
el("div", { className: styles.rowTop },
|
|
6274
|
+
el("strong", { className: styles.name, title: entry.moduleName }, moduleShortName(entry.moduleName)),
|
|
6275
|
+
entry.extra === true ? el("span", { className: styles.tag, "data-user": "true" }, t("extraTag")) : null),
|
|
6276
|
+
el("div", { className: styles.meta }, el("code", null, String(entry.moduleName ?? ""))),
|
|
6277
|
+
annotationBlock(entry)))))))
|
|
6278
|
+
: null,
|
|
5321
6279
|
gateOpen
|
|
5322
6280
|
? el("div", { className: styles.modalBackdrop, onClick: (event) => { if (event.target === event.currentTarget) setGateOpen(false); } },
|
|
5323
6281
|
el("div", { className: styles.modalCard, style: { maxWidth: 520 } },
|
|
@@ -5424,7 +6382,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5424
6382
|
aiJob === null || (aiJob.status === "running" && aiJob.stage === "planning")
|
|
5425
6383
|
? el("div", { style: { flexDirection: "column", gap: "10px", display: "flex" } },
|
|
5426
6384
|
el("section", null,
|
|
5427
|
-
el("input", { type: "text", placeholder: t("aiEmpowerSrcTitle"), value: aiSource, disabled: aiRunning, onChange: (event) => setAiSource(event.
|
|
6385
|
+
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
6386
|
el("div", { className: styles.rowTop },
|
|
5429
6387
|
el("button", { type: "button", className: styles.toggle, disabled: aiRunning || aiSource.trim() === "", onClick: aiStartPlan }, t("aiEmpowerLabel")),
|
|
5430
6388
|
aiRunning ? el("span", { className: styles.message }, t("aiEmpowerWaiting") + (aiJob?.source ? "(" + aiJob.source + ")" : "")) : null,
|
|
@@ -5447,7 +6405,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5447
6405
|
el("div", { style: { flexDirection: "column", gap: "6px", display: "flex", maxHeight: 220, overflowY: "auto" } },
|
|
5448
6406
|
(aiJob.steps ?? []).map((s) =>
|
|
5449
6407
|
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.
|
|
6408
|
+
el("input", { type: "checkbox", checked: aiSelected[s.index] === true, onChange: (event) => setAiSelected((prev) => ({ ...prev, [s.index]: event.target.checked })) }),
|
|
5451
6409
|
el("span", null, (s.index + 1) + ". [" + s.action + "] " + (s.description ?? "") + (s.path ? " → " + s.path : "")))),
|
|
5452
6410
|
el("div", { className: styles.rowTop },
|
|
5453
6411
|
el("button", { type: "button", className: styles.toggle, onClick: aiRun }, t("aiEmpowerRun")),
|
|
@@ -5468,10 +6426,10 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5468
6426
|
el("p", { className: styles.message }, repoLandTitleText()),
|
|
5469
6427
|
el("div", { className: styles.rowTop, style: { flexWrap: "wrap" } },
|
|
5470
6428
|
el("span", { className: styles.message }, t("repoLandDirLabel") + ":" + (repoLandDir || "—")),
|
|
5471
|
-
el("input", { type: "text", value: repoLandDirInput, onChange: (event) => setRepoLandDirInput(event.
|
|
6429
|
+
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
6430
|
el("button", { type: "button", className: styles.toggle, onClick: repoLandSaveDir }, t("repoLandSaveDir"))),
|
|
5473
6431
|
el("section", null,
|
|
5474
|
-
el("input", { type: "text", placeholder: t("repoLandPlaceholder"), value: repoLandInput, disabled: repoLandBusy, onChange: (event) => setRepoLandInput(event.
|
|
6432
|
+
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
6433
|
el("div", { className: styles.rowTop },
|
|
5476
6434
|
el("button", { type: "button", className: styles.toggle, disabled: repoLandBusy || repoLandInput.trim() === "", onClick: repoLandStart }, repoLandBusy ? t("repoLandBusy") : t("repoLandLabel"))),
|
|
5477
6435
|
repoLandResult !== null
|
|
@@ -5728,7 +6686,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5728
6686
|
type: "password",
|
|
5729
6687
|
placeholder: t("githubTokenPlaceholder"),
|
|
5730
6688
|
value: ghToken,
|
|
5731
|
-
onChange: (event) => setGhToken(event.
|
|
6689
|
+
onChange: (event) => setGhToken(event.target.value),
|
|
5732
6690
|
style: { width: "100%", boxSizing: "border-box" },
|
|
5733
6691
|
}),
|
|
5734
6692
|
el("button", { type: "button", className: styles.toggle, disabled: ghLoginBusy || ghToken.trim() === "", onClick: githubLogin }, t("githubLoginBtn")))
|
|
@@ -5811,6 +6769,16 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5811
6769
|
toolbarOpen
|
|
5812
6770
|
? el("div", { className: styles.toolbarGroup, style: (dragPos ?? toolbarPos) !== null ? { left: (dragPos ?? toolbarPos).left, top: (dragPos ?? toolbarPos).top + 36, right: "auto" } : undefined },
|
|
5813
6771
|
|
|
6772
|
+
el("button", {
|
|
6773
|
+
type: "button",
|
|
6774
|
+
className: styles.toolItem,
|
|
6775
|
+
"data-active": annotationsPanelOpen ? "true" : "false",
|
|
6776
|
+
title: t("annotationsPanelTitle"),
|
|
6777
|
+
"aria-label": t("annotationsBtn"),
|
|
6778
|
+
// issue #22 ②③:打开即读一次(注解是独立文件,别处改了也能看到最新的);
|
|
6779
|
+
// ★ 0.5.93:打开时列表回到**收起**状态(见 openAnnotationsPanel)
|
|
6780
|
+
onClick: () => toggleAnnotationsPanel(),
|
|
6781
|
+
}, t("annotationsBtn") + (annotations !== null && annotations.stats !== null && annotations.stats !== undefined && annotations.stats.packages > 0 ? " " + annotations.stats.packages : "")),
|
|
5814
6782
|
hostedByShell ? null : el("button", { // ★ 0.5.84:桌面端(shell 托管)不显示;web 端保留且可用
|
|
5815
6783
|
type: "button",
|
|
5816
6784
|
className: styles.toolItem,
|
|
@@ -5965,7 +6933,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
5965
6933
|
: t("searchPlaceholderOther").replace("{source}", sourceDisplayName(searchSource))),
|
|
5966
6934
|
"aria-label": t("search"),
|
|
5967
6935
|
onChange: (event) => {
|
|
5968
|
-
const v = event.
|
|
6936
|
+
const v = event.target.value;
|
|
5969
6937
|
setQuery(v);
|
|
5970
6938
|
try { localStorage.setItem("pc-market-query", v); } catch {}
|
|
5971
6939
|
},
|
|
@@ -6183,6 +7151,49 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6183
7151
|
el("h3", null, mode === "skills" ? t("installedSkillsTitle") : t("installedTitle")),
|
|
6184
7152
|
mode === "plugins"
|
|
6185
7153
|
? el("button", { type: "button", className: styles.toggle, onClick: () => setInstalledSearchOpen((v) => !v) }, t("search"))
|
|
7154
|
+
: null,
|
|
7155
|
+
// ★ issue #22 ①:一键检测全部更新(**只有图标**的圆按钮 ↑↑↑,与「搜索」同高)。
|
|
7156
|
+
// 三态都在按钮上:idle(可点)/ checking(转圈 + 禁用)/ done(描边高亮,再点是重查)。
|
|
7157
|
+
// 判据全在服务端(唯一判据 + 并发上限),这里只负责"问一句、如实显示"。
|
|
7158
|
+
mode === "plugins"
|
|
7159
|
+
? el("button", {
|
|
7160
|
+
type: "button",
|
|
7161
|
+
className: styles.iconBtn,
|
|
7162
|
+
title: batchCheck !== null && batchCheck.status === "checking"
|
|
7163
|
+
? t("checkingAllUpdates").replace("{n}", String(batchCheck.total ?? 0))
|
|
7164
|
+
: t("checkAllUpdates") + ":" + t("checkAllUpdatesHint").replace("{n}", String(state.status === "ready" ? state.data.entries.length : 0)),
|
|
7165
|
+
"aria-label": t("checkAllUpdates"),
|
|
7166
|
+
"data-state": batchCheck !== null && batchCheck.status === "checking" ? "checking" : (batchCheck !== null && batchCheck.status === "ready" ? "done" : "idle"),
|
|
7167
|
+
disabled: (batchCheck !== null && batchCheck.status === "checking") || state.status !== "ready",
|
|
7168
|
+
onClick: checkAllUpdates,
|
|
7169
|
+
}, batchCheck !== null && batchCheck.status === "checking"
|
|
7170
|
+
? el("span", { className: styles.spinner })
|
|
7171
|
+
: el("svg", { width: 13, height: 13, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.2, strokeLinecap: "round", strokeLinejoin: "round" },
|
|
7172
|
+
el("path", { d: "M5 20V7" }), el("polyline", { points: "3 9 5 7 7 9" }),
|
|
7173
|
+
el("path", { d: "M12 20V7" }), el("polyline", { points: "10 9 12 7 14 9" }),
|
|
7174
|
+
el("path", { d: "M19 20V7" }), el("polyline", { points: "17 9 19 7 21 9" })))
|
|
7175
|
+
: null,
|
|
7176
|
+
// 结果态:可更新 > 0 ⇒ 可点的筛选按钮(同一个筛选的开关);可更新 = 0 ⇒ **只读**小结,
|
|
7177
|
+
// 不做成按钮(否则就是"点了一下列表变空"的死按钮)。
|
|
7178
|
+
mode === "plugins" && batchCheck !== null && batchCheck.status === "ready" && batchCheck.updatable > 0
|
|
7179
|
+
? el("button", {
|
|
7180
|
+
type: "button",
|
|
7181
|
+
className: styles.extraBtn,
|
|
7182
|
+
title: t("batchUpdateResultTip")
|
|
7183
|
+
.replace("{n}", String(batchCheck.updatable))
|
|
7184
|
+
.replace("{m}", String(Math.max(0, batchCheck.checked - batchCheck.updatable - batchCheck.unknown)))
|
|
7185
|
+
.replace("{k}", String(batchCheck.unknown))
|
|
7186
|
+
.replace("{at}", new Date(batchCheck.at).toLocaleTimeString()),
|
|
7187
|
+
"aria-pressed": onlyUpdatable ? "true" : "false",
|
|
7188
|
+
onClick: () => setOnlyUpdatable((v) => !v),
|
|
7189
|
+
}, t("batchUpdateOnly") + " · " + batchCheck.updatable + (onlyUpdatable ? " ✓" : ""))
|
|
7190
|
+
: null,
|
|
7191
|
+
mode === "plugins" && batchCheck !== null && batchCheck.status === "ready" && batchCheck.updatable === 0
|
|
7192
|
+
? 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()) },
|
|
7193
|
+
t("checkAllUpdateNone") + (batchCheck.unknown > 0 ? "(" + t("batchUpdateUnknown") + " " + batchCheck.unknown + ")" : ""))
|
|
7194
|
+
: null,
|
|
7195
|
+
mode === "plugins" && batchCheck !== null && batchCheck.status === "error"
|
|
7196
|
+
? el("span", { className: styles.tag, "data-pending": "true", title: String(batchCheck.error ?? "") }, t("checkAllUpdateFailed"))
|
|
6186
7197
|
: null),
|
|
6187
7198
|
mode === "plugins" && installedSearchOpen
|
|
6188
7199
|
? el("div", { className: styles.search },
|
|
@@ -6193,7 +7204,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6193
7204
|
placeholder: t("searchInstalledPlaceholder"),
|
|
6194
7205
|
"aria-label": t("searchInstalledPlaceholder"),
|
|
6195
7206
|
onChange: (event) => {
|
|
6196
|
-
const v = event.
|
|
7207
|
+
const v = event.target.value;
|
|
6197
7208
|
setInstalledQuery(v);
|
|
6198
7209
|
try { localStorage.setItem("pc-market-installed-q", v); } catch {}
|
|
6199
7210
|
},
|
|
@@ -6273,9 +7284,13 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6273
7284
|
? el("p", { className: styles.status }, t("empty"))
|
|
6274
7285
|
: installedQueryNorm !== "" && rows.length === 0
|
|
6275
7286
|
? el("p", { className: styles.status }, t("noMatch"))
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
7287
|
+
// issue #22 ①:把「仅看可更新」筛空的话必须说清楚**是筛空的**,
|
|
7288
|
+
// 否则用户看到空列表会以为插件没了(并给出取消筛选的办法)。
|
|
7289
|
+
: onlyUpdatable && rows.length === 0
|
|
7290
|
+
? el("p", { className: styles.status }, t("batchUpdateFilterEmpty"))
|
|
7291
|
+
: extraOnly && rows.length === 0
|
|
7292
|
+
? el("p", { className: styles.status }, t("extraEmpty"))
|
|
7293
|
+
: el("ul", { className: styles.list }, rows)),
|
|
6279
7294
|
showBackTop
|
|
6280
7295
|
? el("button", {
|
|
6281
7296
|
type: "button",
|
|
@@ -6323,7 +7338,7 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6323
7338
|
type: "checkbox",
|
|
6324
7339
|
checked: aiRemember,
|
|
6325
7340
|
onChange: (event) => {
|
|
6326
|
-
const checked = event.
|
|
7341
|
+
const checked = event.target.checked;
|
|
6327
7342
|
setAiRemember(checked);
|
|
6328
7343
|
try { localStorage.setItem("pc-ai-remember", checked ? "1" : "0"); } catch {}
|
|
6329
7344
|
},
|
|
@@ -6351,8 +7366,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6351
7366
|
...sourcesData.registries.map((src) =>
|
|
6352
7367
|
editReg !== null && editReg.id === src.id
|
|
6353
7368
|
? 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.
|
|
7369
|
+
el("input", { type: "text", value: editReg.name, onChange: (event) => setEditReg({ ...editReg, name: event.target.value }), style: { flex: 1, minWidth: 80 } }),
|
|
7370
|
+
el("input", { type: "text", value: editReg.url, onChange: (event) => setEditReg({ ...editReg, url: event.target.value }), style: { flex: 2, minWidth: 120 } }),
|
|
6356
7371
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: saveEditReg }, t("saveSource")),
|
|
6357
7372
|
el("button", { type: "button", className: styles.toggle, onClick: () => setEditReg(null) }, t("cancelEdit")))
|
|
6358
7373
|
: el("div", { key: src.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
@@ -6378,8 +7393,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6378
7393
|
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
7394
|
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
7395
|
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.
|
|
7396
|
+
el("input", { type: "text", placeholder: t("sourceName"), value: sourceName, onChange: (event) => setSourceName(event.target.value), style: { flex: 1, minWidth: 0 } }),
|
|
7397
|
+
el("input", { type: "text", placeholder: t("sourceUrl"), value: sourceUrl, onChange: (event) => setSourceUrl(event.target.value), style: { flex: 2, minWidth: 0 } }),
|
|
6383
7398
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: addSource }, t("addSource"))),
|
|
6384
7399
|
el("div", { className: styles.rowTop },
|
|
6385
7400
|
el("button", {
|
|
@@ -6401,8 +7416,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6401
7416
|
(sourcesData.indexSources ?? []).map((s) =>
|
|
6402
7417
|
editIndex !== null && editIndex.id === s.id
|
|
6403
7418
|
? 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.
|
|
7419
|
+
el("input", { type: "text", value: editIndex.name, onChange: (event) => setEditIndex({ ...editIndex, name: event.target.value }), style: { flex: 1, minWidth: 80 } }),
|
|
7420
|
+
el("input", { type: "text", value: editIndex.url, onChange: (event) => setEditIndex({ ...editIndex, url: event.target.value }), style: { flex: 2, minWidth: 120 } }),
|
|
6406
7421
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: saveEditIndex }, t("saveSource")),
|
|
6407
7422
|
el("button", { type: "button", className: styles.toggle, onClick: () => setEditIndex(null) }, t("cancelEdit")))
|
|
6408
7423
|
: el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
@@ -6414,8 +7429,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6414
7429
|
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
7430
|
el("button", { type: "button", className: styles.toggle, title: t("removeSourceHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "remove-index", id: s.id }) }, t("removeSource"))))),
|
|
6416
7431
|
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.
|
|
7432
|
+
el("input", { type: "text", placeholder: t("sourceName"), value: indexSourceName, onChange: (event) => setIndexSourceName(event.target.value), style: { flex: 1, minWidth: 0 } }),
|
|
7433
|
+
el("input", { type: "text", placeholder: t("indexUrlPlaceholder"), value: indexSourceUrl, onChange: (event) => setIndexSourceUrl(event.target.value), style: { flex: 2, minWidth: 0 } }),
|
|
6419
7434
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: addIndexSource }, t("addIndexSource"))),
|
|
6420
7435
|
el("div", { className: styles.rowTop },
|
|
6421
7436
|
el("button", {
|
|
@@ -6433,8 +7448,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6433
7448
|
(sourcesData.gitSources ?? []).map((s) =>
|
|
6434
7449
|
editGit !== null && editGit.id === s.id
|
|
6435
7450
|
? 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.
|
|
7451
|
+
el("input", { type: "text", value: editGit.name, onChange: (event) => setEditGit({ ...editGit, name: event.target.value }), style: { flex: 1, minWidth: 80 } }),
|
|
7452
|
+
el("input", { type: "text", value: editGit.urlTemplate, onChange: (event) => setEditGit({ ...editGit, urlTemplate: event.target.value }), style: { flex: 2, minWidth: 120 } }),
|
|
6438
7453
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: saveEditGit }, t("saveSource")),
|
|
6439
7454
|
el("button", { type: "button", className: styles.toggle, onClick: () => setEditGit(null) }, t("cancelEdit")))
|
|
6440
7455
|
: el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
@@ -6446,8 +7461,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6446
7461
|
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
7462
|
el("button", { type: "button", className: styles.toggle, title: t("removeSourceHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "remove-git", id: s.id }) }, t("removeSource"))))),
|
|
6448
7463
|
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.
|
|
7464
|
+
el("input", { type: "text", placeholder: t("sourceName"), value: gitSourceName, onChange: (event) => setGitSourceName(event.target.value), style: { flex: 1, minWidth: 0 } }),
|
|
7465
|
+
el("input", { type: "text", placeholder: t("gitUrlPlaceholder"), value: gitSourceUrl, onChange: (event) => setGitSourceUrl(event.target.value), style: { flex: 2, minWidth: 0 } }),
|
|
6451
7466
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: addGitSource }, t("addGitSource"))),
|
|
6452
7467
|
]),
|
|
6453
7468
|
accSection("archive", t("archiveSourcesTitle"), [
|
|
@@ -6455,8 +7470,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6455
7470
|
(sourcesData.archiveSources ?? []).map((s) =>
|
|
6456
7471
|
editArchive !== null && editArchive.id === s.id
|
|
6457
7472
|
? 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.
|
|
7473
|
+
el("input", { type: "text", value: editArchive.name, onChange: (event) => setEditArchive({ ...editArchive, name: event.target.value }), style: { flex: 1, minWidth: 80 } }),
|
|
7474
|
+
el("input", { type: "text", value: editArchive.urlTemplate, onChange: (event) => setEditArchive({ ...editArchive, urlTemplate: event.target.value }), style: { flex: 2, minWidth: 120 } }),
|
|
6460
7475
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: saveEditArchive }, t("saveSource")),
|
|
6461
7476
|
el("button", { type: "button", className: styles.toggle, onClick: () => setEditArchive(null) }, t("cancelEdit")))
|
|
6462
7477
|
: el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
@@ -6468,8 +7483,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6468
7483
|
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
7484
|
el("button", { type: "button", className: styles.toggle, title: t("removeSourceHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "remove-archive", id: s.id }) }, t("removeSource"))))),
|
|
6470
7485
|
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.
|
|
7486
|
+
el("input", { type: "text", placeholder: t("sourceName"), value: archiveSourceName, onChange: (event) => setArchiveSourceName(event.target.value), style: { flex: 1, minWidth: 0 } }),
|
|
7487
|
+
el("input", { type: "text", placeholder: t("archiveUrlPlaceholder"), value: archiveSourceUrl, onChange: (event) => setArchiveSourceUrl(event.target.value), style: { flex: 2, minWidth: 0 } }),
|
|
6473
7488
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: addArchiveSource }, t("addArchiveSource"))),
|
|
6474
7489
|
]),
|
|
6475
7490
|
accSection("search", t("searchSourcesTitle"), [
|
|
@@ -6486,14 +7501,14 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6486
7501
|
? el("button", { type: "button", className: styles.toggle, title: t("removeSourceHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "remove-search", id: s.id }) }, t("removeSource"))
|
|
6487
7502
|
: null)),
|
|
6488
7503
|
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.
|
|
7504
|
+
el("input", { type: "text", placeholder: t("sourceName"), value: searchSourceName, onChange: (event) => setSearchSourceName(event.target.value), style: { flex: 1, minWidth: 0 } }),
|
|
7505
|
+
el("input", { type: "text", placeholder: t("searchUrlPlaceholder"), value: searchSourceUrl, onChange: (event) => setSearchSourceUrl(event.target.value), style: { flex: 2, minWidth: 0 } }),
|
|
6491
7506
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: addSearchSource }, t("addSearchSource"))),
|
|
6492
7507
|
el("textarea", {
|
|
6493
7508
|
rows: 2,
|
|
6494
7509
|
placeholder: t("headersPlaceholder"),
|
|
6495
7510
|
value: searchSourceHeaders,
|
|
6496
|
-
onChange: (event) => setSearchSourceHeaders(event.
|
|
7511
|
+
onChange: (event) => setSearchSourceHeaders(event.target.value),
|
|
6497
7512
|
style: { width: "100%", boxSizing: "border-box", font: "inherit", resize: "vertical" },
|
|
6498
7513
|
}),
|
|
6499
7514
|
]),
|
|
@@ -6505,8 +7520,8 @@ const RESTART_FAILED_NOTE = "重启请求失败";
|
|
|
6505
7520
|
? t("giteeLoggedIn") + (giteeStatus.login || "—")
|
|
6506
7521
|
: (giteeStatus !== null && giteeStatus.clientConfigured ? t("giteeSetupHint") : t("giteeLoginPrompt")))),
|
|
6507
7522
|
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.
|
|
7523
|
+
el("input", { type: "text", placeholder: t("giteeClientId"), value: giteeClientId, onChange: (event) => setGiteeClientId(event.target.value), style: { flex: 1, minWidth: 0 } }),
|
|
7524
|
+
el("input", { type: "password", placeholder: t("giteeClientSecret"), value: giteeClientSecret, onChange: (event) => setGiteeClientSecret(event.target.value), style: { flex: 2, minWidth: 0 } }),
|
|
6510
7525
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy || !giteeClientId.trim() || !giteeClientSecret.trim(), onClick: saveGiteeSetup }, t("giteeSave"))),
|
|
6511
7526
|
el("div", { className: styles.rowTop },
|
|
6512
7527
|
el("button", { type: "button", className: styles.toggle, disabled: giteeStatus !== null && !giteeStatus.clientConfigured, onClick: startGiteeOauth }, t("giteeLoginBtn")),
|