@noob-stupid/dsh-plugin-console 0.5.34 → 0.5.35
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 +498 -216
- package/lib/server/domain/market-search.js +242 -0
- package/lib/server/routes/market.js +108 -35
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -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_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)}";
|
|
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)}";
|
|
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");
|
|
@@ -364,6 +364,10 @@ window.__ModuleLoader__.load({
|
|
|
364
364
|
noMatch: "没有匹配的插件。",
|
|
365
365
|
loadMore: "加载更多",
|
|
366
366
|
loadingMore: "加载中…",
|
|
367
|
+
marketCount: "共 {total} 条 / 已显示 {shown} 条",
|
|
368
|
+
marketCountKnown: "已显示 {shown} 条",
|
|
369
|
+
marketTruncated: "结果已截断,可继续加载",
|
|
370
|
+
marketTruncatedEnd: "结果已截断,没有更多了",
|
|
367
371
|
backToSearch: "回到搜索",
|
|
368
372
|
collapseResults: "收起搜索结果",
|
|
369
373
|
expandResults: "展开搜索结果",
|
|
@@ -915,6 +919,10 @@ window.__ModuleLoader__.load({
|
|
|
915
919
|
noMatch: "No matching plugins.",
|
|
916
920
|
loadMore: "Load more",
|
|
917
921
|
loadingMore: "Loading…",
|
|
922
|
+
marketCount: "{total} found / {shown} shown",
|
|
923
|
+
marketCountKnown: "{shown} shown",
|
|
924
|
+
marketTruncated: "Results truncated — you can keep loading",
|
|
925
|
+
marketTruncatedEnd: "Results truncated — no more available",
|
|
918
926
|
backToSearch: "Back to search",
|
|
919
927
|
collapseResults: "Collapse results",
|
|
920
928
|
expandResults: "Expand results",
|
|
@@ -1202,6 +1210,199 @@ window.__ModuleLoader__.load({
|
|
|
1202
1210
|
const PHASE_KEYS = { pending: "pending", loading: "loadingPhase", active: "active", failed: "failed", unloading: "unloading" };
|
|
1203
1211
|
const GITHUB_API = "https://api.github.com";
|
|
1204
1212
|
const GITHUB_RAW = "https://raw.githubusercontent.com";
|
|
1213
|
+
|
|
1214
|
+
// ★★ 市场搜索的**唯一**排序区(0.5.35)★★
|
|
1215
|
+
// 用户定案:「所有的搜索链,都应该以名字相似为主排序,然后才往下按 star 走」。
|
|
1216
|
+
// 这里是服务端 lib/server/domain/market-search.js 里同一标记块(// <<<market-rank:begin/end>>>)的
|
|
1217
|
+
// **逐字移植**:本文件是单文件打包产物(window.__ModuleLoader__.load),不能 import,
|
|
1218
|
+
// 所以两端共用一份**文本**,由 tests/test-market-rank-canonical.mjs 断言"去缩进后逐字相等"
|
|
1219
|
+
// (改一边不改另一边 = 直接红灯)。排序语义只有这一份判据,任何搜索链都只许调它。
|
|
1220
|
+
// <<<market-rank:begin>>>
|
|
1221
|
+
/** 排序层级(越小越靠前)。Tier 0-6 与用户定案逐条对应;NOT_MATCHED 只是"没有名字/描述命中"。 */
|
|
1222
|
+
const RANK_TIER = Object.freeze({
|
|
1223
|
+
FULL_NAME_EXACT: 0,
|
|
1224
|
+
NAME_EXACT: 1,
|
|
1225
|
+
NAME_PREFIX: 2,
|
|
1226
|
+
NAME_SUBSTRING: 3,
|
|
1227
|
+
NAME_TOKENS: 4,
|
|
1228
|
+
DESCRIPTION_OR_TOPICS: 5,
|
|
1229
|
+
README: 6,
|
|
1230
|
+
NOT_MATCHED: 7,
|
|
1231
|
+
})
|
|
1232
|
+
|
|
1233
|
+
/** 归一化:小写 + 去掉尾部 `.git` / 首尾空白。所有判据都用它,避免大小写与 `.git` 差异漏判。 */
|
|
1234
|
+
function normalizeSearchName(value) {
|
|
1235
|
+
return String(value ?? '').trim().replace(/\.git$/iu, '').toLowerCase()
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
/** 查询词按分隔符切出的 token(≥2 字符,去重保序)——用于 Tier 4「token 全命中」判据。 */
|
|
1239
|
+
function queryTokens(query) {
|
|
1240
|
+
const q = normalizeSearchName(query)
|
|
1241
|
+
if (q === '') return []
|
|
1242
|
+
const out = []
|
|
1243
|
+
for (const tk of q.split(/[\s\-_/.]+/u)) {
|
|
1244
|
+
if (tk.length >= 2 && !out.includes(tk)) out.push(tk)
|
|
1245
|
+
}
|
|
1246
|
+
return out
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
/** 条目的两个名字形态(都归一化):`full` = owner/repo,`short` = 裸名(缺 name 时从 full 取尾段)。 */
|
|
1250
|
+
function itemNameForms(item) {
|
|
1251
|
+
const full = normalizeSearchName(item?.fullName ?? item?.full_name)
|
|
1252
|
+
const short = normalizeSearchName(item?.name) || (full === '' ? '' : (full.split('/').pop() ?? ''))
|
|
1253
|
+
return { full, short }
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
/** 名字里按分隔符切出的 token(小写)。`dsh-connection-card-host` → [dsh, connection, card, host]。 */
|
|
1257
|
+
function nameTokens(name) {
|
|
1258
|
+
return String(name ?? '').toLowerCase().split(/[\s\-_/.]+/u).filter(Boolean)
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
/** description / topics 是不是命中查询词(整词子串,大小写不敏感)。 */
|
|
1262
|
+
function textHitsQuery(query, item) {
|
|
1263
|
+
const q = normalizeSearchName(query)
|
|
1264
|
+
if (q === '') return false
|
|
1265
|
+
const desc = normalizeSearchName(item?.description)
|
|
1266
|
+
if (desc !== '' && desc.includes(q)) return true
|
|
1267
|
+
const topics = Array.isArray(item?.topics) ? item.topics : []
|
|
1268
|
+
return topics.some((topic) => normalizeSearchName(topic).includes(q))
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
/** README 命中:优先看条目**自带**的 readme 文本;没有就认标记位(服务端 in:readme 重查补进来的条目)。
|
|
1272
|
+
* 标记位是"这条是从 README 检索面捞到的"的既成事实,不编造内容。 */
|
|
1273
|
+
function readmeHitsQuery(query, item) {
|
|
1274
|
+
const q = normalizeSearchName(query)
|
|
1275
|
+
if (q === '') return false
|
|
1276
|
+
const readme = normalizeSearchName(item?.readme)
|
|
1277
|
+
if (readme !== '' && readme.includes(q)) return true
|
|
1278
|
+
return item?.viaReadme === true
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/** 名字相似度分层(唯一判据;越小越靠前)。owner/repo 查询只按 full_name 判 Tier 0,
|
|
1282
|
+
* 避免"同名不同 owner"被当成逐字命中(与用户"输入确切名字要排第一"的预期一致)。 */
|
|
1283
|
+
function rankTier(query, item) {
|
|
1284
|
+
const q = normalizeSearchName(query)
|
|
1285
|
+
if (q === '') return RANK_TIER.NOT_MATCHED
|
|
1286
|
+
const { full, short } = itemNameForms(item)
|
|
1287
|
+
const tokens = queryTokens(query)
|
|
1288
|
+
const hasOwner = q.includes('/')
|
|
1289
|
+
const target = short !== '' ? short : full
|
|
1290
|
+
if (full !== '' && full === q) return RANK_TIER.FULL_NAME_EXACT
|
|
1291
|
+
if (!hasOwner && short !== '' && short === q) return RANK_TIER.NAME_EXACT
|
|
1292
|
+
if (target !== '' && target.startsWith(q)) return RANK_TIER.NAME_PREFIX
|
|
1293
|
+
if (target !== '' && target.includes(q)) return RANK_TIER.NAME_SUBSTRING
|
|
1294
|
+
if (tokens.length > 0) {
|
|
1295
|
+
const owned = nameTokens(full !== '' ? full : short)
|
|
1296
|
+
if (tokens.every((tk) => owned.includes(tk))) return RANK_TIER.NAME_TOKENS
|
|
1297
|
+
}
|
|
1298
|
+
if (textHitsQuery(query, item)) return RANK_TIER.DESCRIPTION_OR_TOPICS
|
|
1299
|
+
if (readmeHitsQuery(query, item)) return RANK_TIER.README
|
|
1300
|
+
return RANK_TIER.NOT_MATCHED
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
/** 星数(缺省 0;非数字按 0 处理——索引/GitHub 都是外部数据,不能拿它当数字用)。 */
|
|
1304
|
+
function starsOf(item) {
|
|
1305
|
+
const n = item?.stars ?? item?.stargazers_count
|
|
1306
|
+
return typeof n === 'number' && Number.isFinite(n) ? n : 0
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
/** 更新时间戳(毫秒)。缺省/非法一律 0("没给时间"排最后;绝不编造"刚更新")。 */
|
|
1310
|
+
function updatedAtOf(item) {
|
|
1311
|
+
const raw = item?.updatedAt ?? item?.updated_at
|
|
1312
|
+
if (typeof raw === 'number' && Number.isFinite(raw)) return raw
|
|
1313
|
+
if (typeof raw === 'string' && raw !== '') {
|
|
1314
|
+
const parsed = Date.parse(raw)
|
|
1315
|
+
if (Number.isFinite(parsed)) return parsed
|
|
1316
|
+
}
|
|
1317
|
+
return 0
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/** 确定性兜底 tiebreaker:同层同星同时间时按 full_name 字典序(分页不抖动)。 */
|
|
1321
|
+
function nameOf(item) {
|
|
1322
|
+
return normalizeSearchName(item?.fullName ?? item?.full_name ?? item?.name)
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/** 统一比较器:Tier 0-6 升序 → stars 降序 → updatedAt 降序 → full_name 升序。
|
|
1326
|
+
* 「名字相似为主、同层才比 star」的全部语义都在这一个函数里;所有搜索链只许调它。 */
|
|
1327
|
+
function compareByRank(query) {
|
|
1328
|
+
return (a, b) => {
|
|
1329
|
+
const ta = rankTier(query, a)
|
|
1330
|
+
const tb = rankTier(query, b)
|
|
1331
|
+
if (ta !== tb) return ta - tb
|
|
1332
|
+
const sa = starsOf(a)
|
|
1333
|
+
const sb = starsOf(b)
|
|
1334
|
+
if (sa !== sb) return sb - sa
|
|
1335
|
+
const ua = updatedAtOf(a)
|
|
1336
|
+
const ub = updatedAtOf(b)
|
|
1337
|
+
if (ua !== ub) return ub - ua
|
|
1338
|
+
const na = nameOf(a)
|
|
1339
|
+
const nb = nameOf(b)
|
|
1340
|
+
return na < nb ? -1 : (na > nb ? 1 : 0)
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
/** **唯一入口**:按用户定案排序(返回**新数组**;条目对象一个字段都不动 —— 只改顺序不改内容)。
|
|
1345
|
+
* 分页幂等的根本保证:同一数组同一 query 的排序结果逐位相同(比较器无随机、无时间依赖)。 */
|
|
1346
|
+
function rankSearchResults(items, query) {
|
|
1347
|
+
const list = Array.isArray(items) ? items.slice() : []
|
|
1348
|
+
if (list.length < 2) return list
|
|
1349
|
+
return list.sort(compareByRank(query))
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
/** 分层结果(UI 角标/说明与测试用):[{ tier, items }] 只含**真的命中**(Tier 0-6)的层,层序固定。 */
|
|
1353
|
+
function rankTiers(items, query) {
|
|
1354
|
+
const buckets = new Map()
|
|
1355
|
+
for (const item of Array.isArray(items) ? items : []) {
|
|
1356
|
+
const tier = rankTier(query, item)
|
|
1357
|
+
if (tier > RANK_TIER.README) continue
|
|
1358
|
+
if (!buckets.has(tier)) buckets.set(tier, [])
|
|
1359
|
+
buckets.get(tier).push(item)
|
|
1360
|
+
}
|
|
1361
|
+
return [...buckets.keys()].sort((a, b) => a - b).map((tier) => ({ tier, items: buckets.get(tier) }))
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
/**
|
|
1365
|
+
* 分页 + 截断口径(纯函数;GitHub search 的 `total_count` 与 `per_page` 都进这里):
|
|
1366
|
+
* · shown 本次实际显示的条目数
|
|
1367
|
+
* · total 数据源报的**总命中数**(未知传 null → UI 只说"已显示 M 条",绝不编造总数)
|
|
1368
|
+
* · hasMore 还能不能往下翻(`page*perPage < total` **且没有翻过数据源硬上限**;总数为 null 时退化为"本页满页")
|
|
1369
|
+
* · truncated 结果被截断了(总数 > 本次已取到的范围)——UI 必须**明说**,不许让用户以为"就是没有"
|
|
1370
|
+
* · reason 截断的**可读原因**(GitHub API 最多 1000 条 / 索引封顶 / 无 total 但满页)
|
|
1371
|
+
* 幂等:同一 (page, perPage, total) 永远同一结论。
|
|
1372
|
+
*
|
|
1373
|
+
* ★ 0.5.35 实测(真机 GitHub,2026-10-03):`per_page=20&page=51`(第 1001 条起)不是"返回空"而是
|
|
1374
|
+
* **HTTP 422 `Only the first 1000 search results are available`** —— 也就是说越过上限再翻页必然报错。
|
|
1375
|
+
* 所以 hasMore 在 `page*perPage >= min(total, cap)` 时必须为 false:按钮不给,用户就不会点出一个错误。
|
|
1376
|
+
* 它也在标记块**之内**:浏览器那一半用同一份口径算「加载更多」与「共 N 条」。
|
|
1377
|
+
*/
|
|
1378
|
+
function buildPageEnvelope({ shown = 0, total = null, page = 1, perPage = 20, cap = 1000, capReason = null } = {}) {
|
|
1379
|
+
const n = Number.isFinite(shown) && shown > 0 ? shown : 0
|
|
1380
|
+
const p = Number.isFinite(page) && page > 0 ? Math.floor(page) : 1
|
|
1381
|
+
const size = Number.isFinite(perPage) && perPage > 0 ? Math.floor(perPage) : 20
|
|
1382
|
+
const known = typeof total === 'number' && Number.isFinite(total) && total >= 0 ? Math.floor(total) : null
|
|
1383
|
+
const capValue = Number.isFinite(cap) && cap > 0 ? Math.floor(cap) : Infinity
|
|
1384
|
+
const fetched = p * size
|
|
1385
|
+
// 数据源硬上限之内还能取到条数(GitHub search 只有前 1000 条;越过就是 422,不是空页)
|
|
1386
|
+
const reachable = known === null ? Infinity : Math.min(known, capValue)
|
|
1387
|
+
// hasMore 只由**总数与上限**决定(`shown > 0`):本页返回 0 条就是到底了,不许再让用户点空按钮,
|
|
1388
|
+
// 也不许让用户点到必然 422 的那一页。
|
|
1389
|
+
const hasMore = known === null ? n >= size : (n > 0 && fetched < reachable)
|
|
1390
|
+
// 截断分两种,都必须让用户看见(而不是以为"就是没有"):
|
|
1391
|
+
// ① 数据源硬上限(GitHub search 只给前 1000 条)→ 这是**始终**成立的事实,与翻到第几页无关;
|
|
1392
|
+
// ② 本次只取到前 fetched 条(还有更多没取)→ 翻页可解。
|
|
1393
|
+
const overCap = known !== null && known > cap
|
|
1394
|
+
const truncated = overCap || (known === null ? n >= size : known > fetched)
|
|
1395
|
+
let reason = null
|
|
1396
|
+
if (overCap) reason = capReason !== null ? capReason : `数据源最多只返回前 ${cap} 条(命中 ${known} 条)`
|
|
1397
|
+
else if (known === null && n >= size) reason = '数据源未返回总数(只能按每页满页判断还有更多)'
|
|
1398
|
+
else if (known !== null && known > fetched) reason = `共 ${known} 条,本次只取到前 ${Math.min(fetched, known)} 条`
|
|
1399
|
+
// 已经翻到/翻过硬上限:再说"可继续加载"就是骗人(GitHub 第 1001 条起是 422)
|
|
1400
|
+
if (overCap && fetched >= cap) {
|
|
1401
|
+
reason = capReason !== null ? capReason : `数据源最多只返回前 ${cap} 条(命中 ${known} 条):已翻到第 ${fetched} 条,无法再往下`
|
|
1402
|
+
}
|
|
1403
|
+
return { shown: n, total: known, page: p, perPage: size, hasMore, truncated, reason }
|
|
1404
|
+
}
|
|
1405
|
+
// <<<market-rank:end>>>
|
|
1205
1406
|
const el = react.createElement;
|
|
1206
1407
|
function moduleShortName(moduleName) {
|
|
1207
1408
|
return (moduleName.startsWith("@") ? moduleName.slice(moduleName.indexOf("/") + 1) : moduleName)
|
|
@@ -1263,9 +1464,9 @@ window.__ModuleLoader__.load({
|
|
|
1263
1464
|
return data;
|
|
1264
1465
|
}
|
|
1265
1466
|
/** 执行「建议动作」:与 call() 的关键差别 ——**服务端把"动作失败"也当结果返回**
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1467
|
+
* (`{ ok:false, partial:true, exitCode, reason, stderr }`),而 call() 的统一约定会把
|
|
1468
|
+
* `data.ok === false` 抛成一句没信息量的 "HTTP 200"(真机体验:失败原因看不见)。
|
|
1469
|
+
* 所以这里直接读响应体,只把"协议级失败(HTTP != 2xx / 网络异常)"当错误抛。 */
|
|
1269
1470
|
async function callAction(payload) {
|
|
1270
1471
|
const response = await fetch("/plugin-console/run-suggested", {
|
|
1271
1472
|
method: "POST",
|
|
@@ -1284,7 +1485,7 @@ window.__ModuleLoader__.load({
|
|
|
1284
1485
|
try { void navigator.clipboard.writeText(String(text)); return true } catch { return false }
|
|
1285
1486
|
}
|
|
1286
1487
|
/** 重启服务(按钮 / 工具项 / 自更新后共用):服务端在「本实例由桌面端外壳托管」等场景会**拒绝**
|
|
1287
|
-
|
|
1488
|
+
* 并回结构化短句 —— 必须把它显示出来,否则按钮看起来是「点了没反应」(旧实现把错误吞掉了)。 */
|
|
1288
1489
|
function restartServiceViaServer(setMessage, options) {
|
|
1289
1490
|
const reload = (options ?? {}).reload !== false;
|
|
1290
1491
|
if (reload) setMessage(t("restarting"));
|
|
@@ -1342,12 +1543,14 @@ window.__ModuleLoader__.load({
|
|
|
1342
1543
|
return null;
|
|
1343
1544
|
}
|
|
1344
1545
|
}
|
|
1345
|
-
/** 浏览器直连 GitHub 搜索;失败抛错由调用方回退到服务端通道。
|
|
1546
|
+
/** 浏览器直连 GitHub 搜索;失败抛错由调用方回退到服务端通道。
|
|
1547
|
+
* ★ 0.5.35:结果顺序不再照 GitHub 的 `sort=stars` 直接用 —— 回到调用点过**唯一**排序函数
|
|
1548
|
+
* (名字相似分层,同层才比 star)。`total` 一起带回来,供"共 N 条 / 还有更多"的口径。 */
|
|
1346
1549
|
async function searchFromGithub(q, page, all) {
|
|
1347
1550
|
const base = (q ?? "").trim() || "dsh-plugin";
|
|
1348
1551
|
const query = all ? base : (base + " topic:dsh-plugin");
|
|
1349
1552
|
const data = await githubFetch(`${GITHUB_API}/search/repositories?q=${encodeURIComponent(query)}&sort=stars&order=desc&per_page=20&page=${Math.max(page || 1, 1)}`);
|
|
1350
|
-
|
|
1553
|
+
const items = (data.items ?? []).map((item) => ({
|
|
1351
1554
|
fullName: item.full_name,
|
|
1352
1555
|
description: item.description ?? "",
|
|
1353
1556
|
htmlUrl: item.html_url,
|
|
@@ -1356,6 +1559,8 @@ window.__ModuleLoader__.load({
|
|
|
1356
1559
|
defaultBranch: item.default_branch ?? "main",
|
|
1357
1560
|
topics: item.topics ?? [],
|
|
1358
1561
|
}));
|
|
1562
|
+
const total = typeof data.total_count === "number" ? data.total_count : null;
|
|
1563
|
+
return { items, total };
|
|
1359
1564
|
}
|
|
1360
1565
|
/** 提取 SKILL.md frontmatter 摘要(name / description / whenToUse,与宿主端同规则)。 */
|
|
1361
1566
|
function summarizeSkillFrontmatter(text) {
|
|
@@ -1391,8 +1596,8 @@ window.__ModuleLoader__.load({
|
|
|
1391
1596
|
return { name, description, whenToUse };
|
|
1392
1597
|
}
|
|
1393
1598
|
/** 浏览器直连读取仓库元数据 + package.json + README 摘要。
|
|
1394
|
-
|
|
1395
|
-
|
|
1599
|
+
* 搜索结果自带描述/star/分支信息,seed 存在时跳过 api.github.com 请求
|
|
1600
|
+
* (该接口在黑洞期会挂起很久,是"查看没反应"的根因)。 */
|
|
1396
1601
|
async function repoInfoFromGithub(repo, seed) {
|
|
1397
1602
|
let branch = "main";
|
|
1398
1603
|
let metaDescription = "";
|
|
@@ -1499,7 +1704,7 @@ window.__ModuleLoader__.load({
|
|
|
1499
1704
|
}
|
|
1500
1705
|
/** 批量识别搜索结果:官方通道(根包 dsh.bundle)/ 聚合仓库(private+workspaces,子包才是插件)/ 普通项目。 */
|
|
1501
1706
|
/** 补标「官方 / 聚合」标记。每条要发网络请求(raw 多通道竞速),
|
|
1502
|
-
|
|
1707
|
+
* 因此限制并发(默认 6 条一批),避免打满浏览器连接数拖慢整页。 */
|
|
1503
1708
|
async function enrichOfficialBundle(items, concurrency = 6) {
|
|
1504
1709
|
const out = [];
|
|
1505
1710
|
for (let i = 0; i < items.length; i += concurrency) {
|
|
@@ -1587,7 +1792,7 @@ window.__ModuleLoader__.load({
|
|
|
1587
1792
|
};
|
|
1588
1793
|
}
|
|
1589
1794
|
/** 服务端 /enrich 兜底:并发限流 12 + 24h 缓存,补齐客户端直连失败/遗漏的标记
|
|
1590
|
-
|
|
1795
|
+
* (每次最多 30 条,按需分批;结果按位置合并回索引与当前展示切片)。 */
|
|
1591
1796
|
function enrichRangeServer(from, slice) {
|
|
1592
1797
|
const BATCH = 30;
|
|
1593
1798
|
for (let i = 0; i < slice.length; i += BATCH) {
|
|
@@ -2533,50 +2738,52 @@ window.__ModuleLoader__.load({
|
|
|
2533
2738
|
(error) => setMessage(t("safetySwitchFailed") + ":" + friendlyGithubError(error).message),
|
|
2534
2739
|
);
|
|
2535
2740
|
};
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2741
|
+
// 0.5.35 改错(**用户"第二次搜索变成一大堆高星无关仓库"的元凶**):这里原来有一套
|
|
2742
|
+
// 「本地索引模糊匹配 + 编辑距离 ≤2」,命中就直接 return —— 它把 12 条**按星数排**的无关条目
|
|
2743
|
+
// 当最终结果(用户搜 dsh-connection-card-host,索引里根本没有这个新仓库,靠 token 相似
|
|
2744
|
+
// 捞出 dsh-desktop ★29812 这类),既埋没了逐字同名,也把「加载更多」变成了死路
|
|
2745
|
+
// (只有 12 条,按钮的 length>=20 条件不成立)。
|
|
2746
|
+
// 判据统一到唯一的名字分层排序(本文件上方 <<<market-rank>>> 区 + 服务端同一份),
|
|
2747
|
+
// 非空查询一律走真实搜索链(浏览器直连 → 失败回落服务端),不再有"索引抢先 return"。
|
|
2748
|
+
/** 服务端/直连结果里的分页口径(纯函数移植:与服务端 buildPageEnvelope 同一份)。
|
|
2749
|
+
* 返回 null 表示"这个数据源没给口径"(不是"没有更多")——调用方保留既有提示,不谎报。 */
|
|
2750
|
+
const envelopeOf = (data, page, perPage = 20) => {
|
|
2751
|
+
if (data === null || typeof data !== "object" || typeof data.hasMore !== "boolean") return null;
|
|
2752
|
+
return {
|
|
2753
|
+
shown: typeof data.shown === "number" ? data.shown : 0,
|
|
2754
|
+
total: typeof data.total === "number" ? data.total : null,
|
|
2755
|
+
hasMore: data.hasMore,
|
|
2756
|
+
truncated: data.truncated === true,
|
|
2757
|
+
reason: typeof data.reason === "string" && data.reason !== "" ? data.reason : null,
|
|
2758
|
+
page,
|
|
2759
|
+
perPage,
|
|
2760
|
+
};
|
|
2552
2761
|
};
|
|
2553
|
-
|
|
2554
|
-
const
|
|
2555
|
-
const
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
if (best <= 2) scored.push({ it, s: 1000 - best * 100 });
|
|
2572
|
-
}
|
|
2573
|
-
scored.sort((a, b) => b.s - a.s);
|
|
2574
|
-
return scored.slice(0, 12).map((x) => x.it);
|
|
2762
|
+
/** 浏览器直连 GitHub 只有 total_count/per_page 可依据:用**同一个** buildPageEnvelope 算口径。 */
|
|
2763
|
+
const envelopeFromGithub = (total, shownCount, page, perPage = 20) => {
|
|
2764
|
+
const env = buildPageEnvelope({
|
|
2765
|
+
shown: typeof shownCount === "number" ? shownCount : 0,
|
|
2766
|
+
total: typeof total === "number" ? total : null, page, perPage,
|
|
2767
|
+
});
|
|
2768
|
+
return { shown: env.shown, total: env.total, hasMore: env.hasMore, truncated: env.truncated, reason: env.reason, page: env.page, perPage: env.perPage };
|
|
2769
|
+
};
|
|
2770
|
+
/** 本地索引浏览切片的口径(索引是本地的,总数就取索引自身长度,绝不编造)。 */
|
|
2771
|
+
const envelopeFromIndex = (list, shownNow, page, perPage = 50) => {
|
|
2772
|
+
const env = buildPageEnvelope({ shown: shownNow, total: Array.isArray(list) ? list.length : null, page, perPage });
|
|
2773
|
+
return { shown: env.shown, total: env.total, hasMore: env.hasMore, truncated: env.truncated, reason: env.reason, page: env.page, perPage: env.perPage };
|
|
2774
|
+
};
|
|
2775
|
+
/** 合并/补标之后重算"已显示 M 条"(total/hasMore/truncated 是数据源的事实,不因合并而改)。 */
|
|
2776
|
+
const repaginate = (hints, list) => {
|
|
2777
|
+
if (hints === null || hints === undefined) return hints;
|
|
2778
|
+
const shown = Array.isArray(list) ? list.length : 0;
|
|
2779
|
+
return { ...hints, shown };
|
|
2575
2780
|
};
|
|
2576
2781
|
/** 合并服务端"增量检索"结果(npm 包名映射 / in:readme 重查 / 代码搜索子包):
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2782
|
+
* npm 命中置顶,其余按增量顺序追加,最后接原有直连结果,全部按 fullName 去重。
|
|
2783
|
+
* 背景(2026-09-20):浏览器直连 GitHub 搜索成功时不会走服务端路由,而未登录用户恰恰只能走直连
|
|
2784
|
+
* →「只存在于 npm 包名 / README / 仓库文件里」的名字(如 web-all)永远补不上。
|
|
2785
|
+
* 0.5.35:合并后的**最终顺序**由唯一排序函数(rankSearchResults)决定 —— 这里的 npm 前置
|
|
2786
|
+
* 只是"合并时别把 npm 命中丢掉",不再冒充排序判据。 */
|
|
2580
2787
|
function mergeSearchExtras(base, extras) {
|
|
2581
2788
|
const seen = new Set();
|
|
2582
2789
|
const npm = [];
|
|
@@ -2597,13 +2804,19 @@ window.__ModuleLoader__.load({
|
|
|
2597
2804
|
return out;
|
|
2598
2805
|
}
|
|
2599
2806
|
/** 用 enrich 结果**就地更新**市场列表(按 fullName 打补丁),而不是整表替换——
|
|
2600
|
-
|
|
2807
|
+
* 否则并行补进来的「增量检索」条目(npm 包名/子包)会被 enrich 回调冲掉。 */
|
|
2601
2808
|
const applyEnriched = (enriched) => setMarket((m) => {
|
|
2602
2809
|
if (m === null || m.status !== "ready" || !Array.isArray(enriched)) return m;
|
|
2603
2810
|
const byName = new Map(enriched.map((x) => [x.fullName, x]));
|
|
2604
2811
|
return { ...m, data: m.data.map((x) => byName.get(x.fullName) ?? x) };
|
|
2605
2812
|
});
|
|
2813
|
+
/** 搜索批次序号:每次 search() 递增。**晚到的旧响应一律丢弃** —— 上一版两条链(浏览器直连
|
|
2814
|
+
* 与"增量检索")+ enrich 回调各自 setMarket,点的时机不同就出现"点两次结果不同"的竞态。 */
|
|
2815
|
+
const searchSeqRef = react.useRef(0);
|
|
2606
2816
|
const search = () => {
|
|
2817
|
+
const seq = searchSeqRef.current + 1;
|
|
2818
|
+
searchSeqRef.current = seq;
|
|
2819
|
+
const current = () => searchSeqRef.current === seq;
|
|
2607
2820
|
setMarket({ status: "loading" });
|
|
2608
2821
|
setRepoInfo(null);
|
|
2609
2822
|
setMarketPage(1);
|
|
@@ -2614,36 +2827,39 @@ window.__ModuleLoader__.load({
|
|
|
2614
2827
|
return;
|
|
2615
2828
|
}
|
|
2616
2829
|
if (query.trim() === "" && indexData !== null && indexData.loaded) {
|
|
2617
|
-
|
|
2830
|
+
const shown = indexData.skills.slice(0, indexShown);
|
|
2831
|
+
setMarket({ status: "ready", data: shown, hints: envelopeFromIndex(indexData.skills, shown.length, 1, 50), direct: false, source: "github", fromIndex: true });
|
|
2618
2832
|
return;
|
|
2619
2833
|
}
|
|
2620
2834
|
call("/plugin-console/search", { q: query, page: 1, skills: true }).then(
|
|
2621
|
-
(data) => {
|
|
2622
|
-
|
|
2835
|
+
(data) => {
|
|
2836
|
+
if (!current()) return;
|
|
2837
|
+
const items = rankSearchResults(data.items ?? [], query);
|
|
2838
|
+
const hints = envelopeOf(data, 1) ?? envelopeFromIndex(items, items.length, 1);
|
|
2839
|
+
setMarket({ status: "ready", data: items, hints, direct: false, source: "github", skills: true });
|
|
2840
|
+
},
|
|
2841
|
+
(error) => { if (current()) setMarket({ status: "error", error: friendlyGithubError(error) }); },
|
|
2623
2842
|
);
|
|
2624
2843
|
return;
|
|
2625
2844
|
}
|
|
2626
2845
|
// 静态索引浏览:GitHub 源 + 空查询时展示 dsh-plugin topic 全量索引(秒开、零 API 调用)
|
|
2627
2846
|
if (searchSource === "github" && !multiSource && query.trim() === "" && indexData !== null && indexData.loaded) {
|
|
2628
|
-
|
|
2847
|
+
const shown = indexData.items.slice(0, indexShown);
|
|
2848
|
+
setMarket({ status: "ready", data: shown, hints: envelopeFromIndex(indexData.items, shown.length, 1, 50), direct: false, source: "github", fromIndex: true });
|
|
2629
2849
|
return;
|
|
2630
2850
|
}
|
|
2631
|
-
//
|
|
2632
|
-
//
|
|
2633
|
-
|
|
2634
|
-
const local = fuzzyMatchIndex(query, indexData.items);
|
|
2635
|
-
if (local.length > 0) {
|
|
2636
|
-
setMarket({ status: "ready", data: local, direct: false, source: "github", fromIndex: true, fuzzy: true });
|
|
2637
|
-
return;
|
|
2638
|
-
}
|
|
2639
|
-
}
|
|
2851
|
+
// 0.5.35 改错:这里原来还有一条「非空查询先做本地索引模糊匹配,命中即 return」的链 ——
|
|
2852
|
+
// 它就是"第二次搜索变成一大堆高星无关仓库 / 没有加载更多"的元凶(见上方说明),已删除。
|
|
2853
|
+
// 非空查询现在只有一条链:真实搜索(直连 GitHub → 失败回落服务端),结果统一走名字分层排序。
|
|
2640
2854
|
if (multiSource) {
|
|
2641
2855
|
// 多源汇总:GitHub + 全部自定义源并行(服务端合并,自带标记)
|
|
2642
2856
|
call("/plugin-console/search", { q: query, page: 1, multi: true }).then(
|
|
2643
2857
|
(data) => {
|
|
2644
|
-
|
|
2858
|
+
if (!current()) return;
|
|
2859
|
+
const items = rankSearchResults(data.items ?? [], query);
|
|
2860
|
+
setMarket({ status: "ready", data: items, hints: envelopeOf(data, 1), direct: false, source: "all", multi: true });
|
|
2645
2861
|
},
|
|
2646
|
-
(error) => setMarket({ status: "error", error: friendlyGithubError(error) }),
|
|
2862
|
+
(error) => { if (current()) setMarket({ status: "error", error: friendlyGithubError(error) }); },
|
|
2647
2863
|
);
|
|
2648
2864
|
return;
|
|
2649
2865
|
}
|
|
@@ -2651,27 +2867,34 @@ window.__ModuleLoader__.load({
|
|
|
2651
2867
|
// Gitee / 自定义源:走服务端平台检索(浏览器直连通道对这些平台不可靠)
|
|
2652
2868
|
call("/plugin-console/search", { q: query, page: 1, source: searchSource }).then(
|
|
2653
2869
|
(data) => {
|
|
2654
|
-
|
|
2655
|
-
|
|
2870
|
+
if (!current()) return;
|
|
2871
|
+
const items = rankSearchResults(data.items ?? [], query);
|
|
2872
|
+
setMarket({ status: "ready", data: items, hints: envelopeOf(data, 1), direct: false, source: searchSource });
|
|
2873
|
+
enrichOfficialBundle(items).then((enriched) => {
|
|
2874
|
+
if (!current()) return;
|
|
2656
2875
|
applyEnriched(enriched);
|
|
2657
2876
|
});
|
|
2658
2877
|
},
|
|
2659
|
-
(error) => setMarket({ status: "error", error: friendlyGithubError(error) }),
|
|
2878
|
+
(error) => { if (current()) setMarket({ status: "error", error: friendlyGithubError(error) }); },
|
|
2660
2879
|
);
|
|
2661
2880
|
return;
|
|
2662
2881
|
}
|
|
2663
2882
|
searchFromGithub(query, 1, repoLandOn).then(
|
|
2664
|
-
(
|
|
2665
|
-
|
|
2666
|
-
|
|
2883
|
+
({ items: rawItems, total: rawTotal }) => {
|
|
2884
|
+
if (!current()) return;
|
|
2885
|
+
// 秒出直连数据(**同样过唯一排序函数**:直连链与服务端链的结果顺序必须一致,
|
|
2886
|
+
// 否则"点两次结果不同"的观感还在)
|
|
2887
|
+
const items = rankSearchResults(rawItems, query);
|
|
2888
|
+
setMarket({ status: "ready", data: items, hints: envelopeFromGithub(rawTotal, items.length, 1), direct: true });
|
|
2667
2889
|
// 并行补标记:
|
|
2668
2890
|
// ① 客户端浏览器直连读根包 package.json(快,官方/聚合标记立即可筛)
|
|
2669
|
-
enrichOfficialBundle(
|
|
2891
|
+
enrichOfficialBundle(items).then((enriched) => {
|
|
2892
|
+
if (!current()) return;
|
|
2670
2893
|
applyEnriched(enriched);
|
|
2671
2894
|
});
|
|
2672
2895
|
// ② 服务端 curl 双通道 enrich(聚合子包 dsh.bundle 检查 → aggregateInstallable),后台补更精确标记
|
|
2673
|
-
call("/plugin-console/enrich", { items
|
|
2674
|
-
(r) => { if (r && Array.isArray(r.items)) applyEnriched(r.items); },
|
|
2896
|
+
call("/plugin-console/enrich", { items }).then(
|
|
2897
|
+
(r) => { if (current() && r && Array.isArray(r.items)) applyEnriched(r.items); },
|
|
2675
2898
|
() => {},
|
|
2676
2899
|
);
|
|
2677
2900
|
// ③ 服务端"增量检索"(并行、不阻塞首屏):npm 包名映射 + in:readme 重查 + 代码搜索子包。
|
|
@@ -2679,9 +2902,16 @@ window.__ModuleLoader__.load({
|
|
|
2679
2902
|
if (query.trim() !== "") {
|
|
2680
2903
|
call("/plugin-console/search", { q: query, page: 1, extras: true, all: repoLandOn === true }).then(
|
|
2681
2904
|
(r) => {
|
|
2905
|
+
if (!current()) return;
|
|
2682
2906
|
if (r !== null && r.codeSearchSkipped === true) setMessage(t("searchLoginHint"));
|
|
2683
2907
|
if (r === null || !Array.isArray(r.items) || r.items.length === 0) return;
|
|
2684
|
-
|
|
2908
|
+
// 合并后**再排一次**(唯一排序函数):npm/README 命中该进哪一层由分层判据决定,
|
|
2909
|
+
// 不由"谁是后到的"决定。
|
|
2910
|
+
setMarket((m) => {
|
|
2911
|
+
if (m === null || m.status !== "ready") return m;
|
|
2912
|
+
const merged = rankSearchResults(mergeSearchExtras(m.data, r.items), query);
|
|
2913
|
+
return { ...m, data: merged, hints: repaginate(m.hints, merged) };
|
|
2914
|
+
});
|
|
2685
2915
|
},
|
|
2686
2916
|
() => {},
|
|
2687
2917
|
);
|
|
@@ -2689,34 +2919,52 @@ window.__ModuleLoader__.load({
|
|
|
2689
2919
|
},
|
|
2690
2920
|
() => call("/plugin-console/search", { q: query, page: 1 }).then(
|
|
2691
2921
|
(data) => {
|
|
2692
|
-
|
|
2693
|
-
|
|
2922
|
+
if (!current()) return;
|
|
2923
|
+
const items = rankSearchResults(data.items ?? [], query);
|
|
2924
|
+
setMarket({ status: "ready", data: items, hints: envelopeOf(data, 1), direct: false });
|
|
2694
2925
|
enrichOfficialBundle(items).then((enriched) => {
|
|
2926
|
+
if (!current()) return;
|
|
2695
2927
|
applyEnriched(enriched);
|
|
2696
2928
|
});
|
|
2697
2929
|
},
|
|
2698
|
-
(error) => setMarket({ status: "error", error: friendlyGithubError(error) }),
|
|
2930
|
+
(error) => { if (current()) setMarket({ status: "error", error: friendlyGithubError(error) }); },
|
|
2699
2931
|
),
|
|
2700
2932
|
);
|
|
2701
2933
|
};
|
|
2934
|
+
/** 翻页合并:按 fullName 去重("+N 条"的新条目才追加)→ 全表**再排一次**(唯一排序函数,
|
|
2935
|
+
* 所以跨页顺序与数据源给的单页顺序一致)→ 重算口径(total/hasMore/truncated 是数据源事实)。 */
|
|
2936
|
+
const appendPage = (prevList, pageItems, baseHints, mergeHints, page, perPage = 20) => {
|
|
2937
|
+
const seen = new Set();
|
|
2938
|
+
const merged = [];
|
|
2939
|
+
for (const item of [...(Array.isArray(prevList) ? prevList : []), ...(Array.isArray(pageItems) ? pageItems : [])]) {
|
|
2940
|
+
const key = item?.fullName ?? JSON.stringify(item);
|
|
2941
|
+
if (seen.has(key)) continue;
|
|
2942
|
+
seen.add(key);
|
|
2943
|
+
merged.push(item);
|
|
2944
|
+
}
|
|
2945
|
+
const ranked = rankSearchResults(merged, query);
|
|
2946
|
+
const src = mergeHints ?? baseHints;
|
|
2947
|
+
const hints = src === null || src === undefined
|
|
2948
|
+
? null
|
|
2949
|
+
: { ...src, shown: ranked.length, page, perPage };
|
|
2950
|
+
return { data: ranked, hints };
|
|
2951
|
+
};
|
|
2702
2952
|
const loadMore = () => {
|
|
2703
2953
|
if (loadingMore || market === null || market.status !== "ready") return;
|
|
2704
2954
|
const next = marketPage + 1;
|
|
2955
|
+
const seq = searchSeqRef.current;
|
|
2956
|
+
const current = () => searchSeqRef.current === seq;
|
|
2705
2957
|
setLoadingMore(true);
|
|
2706
2958
|
setMarketPage(next);
|
|
2959
|
+
const baseHints = market.hints ?? null;
|
|
2707
2960
|
if (mode === "skills" && !market.fromIndex) {
|
|
2708
|
-
// 技能搜索分页:服务端三 topic
|
|
2961
|
+
// 技能搜索分页:服务端三 topic 并集池切片(skills: true)
|
|
2709
2962
|
call("/plugin-console/search", { q: query, page: next, skills: true }).then(
|
|
2710
2963
|
(data) => {
|
|
2711
|
-
|
|
2712
|
-
const
|
|
2713
|
-
const
|
|
2714
|
-
|
|
2715
|
-
if (seen.has(item.fullName)) continue;
|
|
2716
|
-
seen.add(item.fullName);
|
|
2717
|
-
deduped.push(item);
|
|
2718
|
-
}
|
|
2719
|
-
setMarket({ status: "ready", data: deduped, direct: false, source: "github", skills: true });
|
|
2964
|
+
if (!current()) { setLoadingMore(false); return; }
|
|
2965
|
+
const hints = envelopeOf(data, next) ?? envelopeFromIndex(market.data, market.data.length, next);
|
|
2966
|
+
const page_ = appendPage(market.data, data.items, baseHints, hints, next);
|
|
2967
|
+
setMarket({ status: "ready", data: page_.data, hints: page_.hints, direct: false, source: "github", skills: true });
|
|
2720
2968
|
setLoadingMore(false);
|
|
2721
2969
|
},
|
|
2722
2970
|
(error) => {
|
|
@@ -2730,7 +2978,10 @@ window.__ModuleLoader__.load({
|
|
|
2730
2978
|
// 索引浏览:本地继续展示更多(技能模式浏览技能索引段)
|
|
2731
2979
|
const sourceData = mode === "skills" ? indexData.skills : indexData.items;
|
|
2732
2980
|
const more = sourceData.slice(0, indexShown + next * 50);
|
|
2733
|
-
setMarket({
|
|
2981
|
+
setMarket({
|
|
2982
|
+
status: "ready", data: more, direct: false, source: "github", fromIndex: true,
|
|
2983
|
+
hints: envelopeFromIndex(sourceData, more.length, next, 50),
|
|
2984
|
+
});
|
|
2734
2985
|
setIndexShown(indexShown + next * 50);
|
|
2735
2986
|
// 新增的这段继续补「官方/聚合」标记(与首屏同样的增量策略)
|
|
2736
2987
|
if (mode !== "skills") enrichRange(enrichedUpTo, indexShown + next * 50, indexData.items);
|
|
@@ -2740,7 +2991,10 @@ window.__ModuleLoader__.load({
|
|
|
2740
2991
|
if (multiSource) {
|
|
2741
2992
|
call("/plugin-console/search", { q: query, page: next, multi: true }).then(
|
|
2742
2993
|
(data) => {
|
|
2743
|
-
|
|
2994
|
+
if (!current()) { setLoadingMore(false); return; }
|
|
2995
|
+
const hints = envelopeOf(data, next) ?? envelopeFromIndex(market.data, market.data.length, next);
|
|
2996
|
+
const page_ = appendPage(market.data, data.items, baseHints, hints, next);
|
|
2997
|
+
setMarket({ status: "ready", data: page_.data, hints: page_.hints, direct: false, source: "all", multi: true });
|
|
2744
2998
|
setLoadingMore(false);
|
|
2745
2999
|
},
|
|
2746
3000
|
(error) => {
|
|
@@ -2753,11 +3007,16 @@ window.__ModuleLoader__.load({
|
|
|
2753
3007
|
if (searchSource !== "github") {
|
|
2754
3008
|
call("/plugin-console/search", { q: query, page: next, source: searchSource }).then(
|
|
2755
3009
|
(data) => {
|
|
2756
|
-
|
|
3010
|
+
if (!current()) { setLoadingMore(false); return; }
|
|
3011
|
+
const hints = envelopeOf(data, next) ?? envelopeFromIndex(market.data, market.data.length, next);
|
|
3012
|
+
const page_ = appendPage(market.data, data.items, baseHints, hints, next);
|
|
3013
|
+
setMarket({ status: "ready", data: page_.data, hints: page_.hints, direct: false, source: searchSource });
|
|
2757
3014
|
setLoadingMore(false);
|
|
2758
3015
|
enrichOfficialBundle(data.items).then((enriched) => {
|
|
3016
|
+
if (!current()) return;
|
|
3017
|
+
const byName = new Map(enriched.map((x) => [x.fullName, x]));
|
|
2759
3018
|
setMarket((m) => (m !== null && m.status === "ready"
|
|
2760
|
-
? { ...m, data:
|
|
3019
|
+
? { ...m, data: m.data.map((x) => byName.get(x.fullName) ?? x) }
|
|
2761
3020
|
: m));
|
|
2762
3021
|
});
|
|
2763
3022
|
},
|
|
@@ -2769,33 +3028,38 @@ window.__ModuleLoader__.load({
|
|
|
2769
3028
|
return;
|
|
2770
3029
|
}
|
|
2771
3030
|
searchFromGithub(query, next).then(
|
|
2772
|
-
(
|
|
2773
|
-
|
|
3031
|
+
({ items: rawItems, total: rawTotal }) => {
|
|
3032
|
+
if (!current()) { setLoadingMore(false); return; }
|
|
3033
|
+
const hints = envelopeFromGithub(rawTotal, rawItems.length, next);
|
|
3034
|
+
const page_ = appendPage(market.data, rawItems, baseHints, hints, next);
|
|
3035
|
+
setMarket({ status: "ready", data: page_.data, hints: page_.hints, direct: true });
|
|
2774
3036
|
setLoadingMore(false);
|
|
2775
|
-
// 并行补标记:客户端快速(根包)+
|
|
2776
|
-
|
|
3037
|
+
// 并行补标记:客户端快速(根包)+ 服务端完整(子包检查)。
|
|
3038
|
+
// 按 fullName **就地更新**(不再靠"取末尾 N 条"的假设——排序变了,末尾不一定是新页)。
|
|
3039
|
+
const patchByName = (list) => {
|
|
3040
|
+
const byName = new Map(list.map((x) => [x.fullName, x]));
|
|
2777
3041
|
setMarket((m) => (m !== null && m.status === "ready"
|
|
2778
|
-
? { ...m, data:
|
|
3042
|
+
? { ...m, data: m.data.map((x) => byName.get(x.fullName) ?? x) }
|
|
2779
3043
|
: m));
|
|
2780
|
-
}
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
setMarket((m) => (m !== null && m.status === "ready"
|
|
2785
|
-
? { ...m, data: [...m.data.slice(0, m.data.length - r.items.length), ...r.items] }
|
|
2786
|
-
: m));
|
|
2787
|
-
}
|
|
2788
|
-
},
|
|
3044
|
+
};
|
|
3045
|
+
enrichOfficialBundle(rawItems).then((enriched) => { if (current()) patchByName(enriched); });
|
|
3046
|
+
call("/plugin-console/enrich", { items: rawItems }).then(
|
|
3047
|
+
(r) => { if (current() && r && Array.isArray(r.items)) patchByName(r.items); },
|
|
2789
3048
|
() => {},
|
|
2790
3049
|
);
|
|
2791
3050
|
},
|
|
2792
3051
|
() => call("/plugin-console/search", { q: query, page: next }).then(
|
|
2793
3052
|
(data) => {
|
|
2794
|
-
|
|
3053
|
+
if (!current()) { setLoadingMore(false); return; }
|
|
3054
|
+
const hints = envelopeOf(data, next) ?? envelopeFromIndex(market.data, market.data.length, next);
|
|
3055
|
+
const page_ = appendPage(market.data, data.items, baseHints, hints, next);
|
|
3056
|
+
setMarket({ status: "ready", data: page_.data, hints: page_.hints, direct: false });
|
|
2795
3057
|
setLoadingMore(false);
|
|
2796
3058
|
enrichOfficialBundle(data.items).then((enriched) => {
|
|
3059
|
+
if (!current()) return;
|
|
3060
|
+
const byName = new Map(enriched.map((x) => [x.fullName, x]));
|
|
2797
3061
|
setMarket((m) => (m !== null && m.status === "ready"
|
|
2798
|
-
? { ...m, data:
|
|
3062
|
+
? { ...m, data: m.data.map((x) => byName.get(x.fullName) ?? x) }
|
|
2799
3063
|
: m));
|
|
2800
3064
|
});
|
|
2801
3065
|
},
|
|
@@ -2988,8 +3252,8 @@ window.__ModuleLoader__.load({
|
|
|
2988
3252
|
);
|
|
2989
3253
|
};
|
|
2990
3254
|
/** 安装进度文案:服务端 progress{channel,phase,index,total,name,done} → 一行人话。
|
|
2991
|
-
|
|
2992
|
-
|
|
3255
|
+
* 2026-09-20 真装实测:11 个子包的聚合仓库跑 19 分钟,面板只有"安装中"——用户不知道
|
|
3256
|
+
* 在第几个、还剩几个、当前装的是谁。这里把服务端下发的进度翻成"第 i/n 个:<名字>"。 */
|
|
2993
3257
|
const progressText = (progress) => {
|
|
2994
3258
|
if (progress === null || progress === undefined) return "";
|
|
2995
3259
|
if (progress.done === true) return t("progressDone").replace("{total}", String(progress.total ?? 0));
|
|
@@ -3002,7 +3266,7 @@ window.__ModuleLoader__.load({
|
|
|
3002
3266
|
.replace("{name}", progress.name ?? "");
|
|
3003
3267
|
};
|
|
3004
3268
|
/** 授权倒计时(剩余 mm:ss):服务端只给请求时间 + 超时上限,剩余量在前端算。
|
|
3005
|
-
|
|
3269
|
+
* 轮询每 2s 触发一次重渲染,倒计时随之刷新,不需要额外的定时器。 */
|
|
3006
3270
|
const consentRemaining = (job) => {
|
|
3007
3271
|
const consent = job?.aiConsent ?? null;
|
|
3008
3272
|
if (consent === null || consent.pending !== true || typeof consent.since !== "number") return null;
|
|
@@ -3016,10 +3280,10 @@ window.__ModuleLoader__.load({
|
|
|
3016
3280
|
};
|
|
3017
3281
|
//#region install-diagnosis-lines
|
|
3018
3282
|
/** 安装诊断短句(2026-09-26 加法):把服务端下发的 diagnosis / entryCheck / integrity 折成
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3283
|
+
* **一两行短句**(解释性长文一律不进面板 —— 用户明确要求;长文案只挂 title 悬浮提示)。
|
|
3284
|
+
* 返回 [{ key, text, level, title }]:level 为 null(中性)| "warn" | "error",
|
|
3285
|
+
* 直接映射既有 styles.message + data-warn / data-error 约定,不新增徽章 / 按钮 / class。
|
|
3286
|
+
* 无问题时不产出任何行(不占版面);摘要「已校验 …」按需求作为极短一行保留。 */
|
|
3023
3287
|
const diagLines = (job) => {
|
|
3024
3288
|
const view = job === null || job === undefined ? {} : job;
|
|
3025
3289
|
const lines = [];
|
|
@@ -3108,7 +3372,7 @@ window.__ModuleLoader__.load({
|
|
|
3108
3372
|
title: line.title === null || line.title === undefined ? undefined : line.title,
|
|
3109
3373
|
}, line.text);
|
|
3110
3374
|
/** 安装诊断卡(加法):**装成功但装后校验 / 摘要校验有问题**的作业 → 一张卡片;
|
|
3111
|
-
|
|
3375
|
+
* 无问题(或只有「已校验 …」这类中性行)返回 null,不占版面、不加按钮/徽章。 */
|
|
3112
3376
|
const diagCardEl = (job) => {
|
|
3113
3377
|
const lines = diagLines(job).filter((line) => line.level !== null);
|
|
3114
3378
|
if (lines.length === 0) return null;
|
|
@@ -3327,8 +3591,8 @@ window.__ModuleLoader__.load({
|
|
|
3327
3591
|
};
|
|
3328
3592
|
}, []);
|
|
3329
3593
|
/** 搜索结果卡片上的"添加到本地":直接启动安装任务(服务端解析包名与类型),
|
|
3330
|
-
|
|
3331
|
-
|
|
3594
|
+
* 点击立即出现进度卡片——不再先浏览器取包名(黑洞期直连失败会拖到 40s 无反馈)。
|
|
3595
|
+
* 技能条目走技能通道,套装条目走套装通道。 */
|
|
3332
3596
|
const addLocal = (item) => {
|
|
3333
3597
|
if (installing !== null) return;
|
|
3334
3598
|
setInstalling(item.fullName);
|
|
@@ -4101,25 +4365,25 @@ window.__ModuleLoader__.load({
|
|
|
4101
4365
|
// (老服务端没有该字段时回落 !supported),并给一个手动关闭入口 —— 关闭只影响展示:
|
|
4102
4366
|
// 服务端记的是「这个框架版本我看过了」,版本一变会重新提示;supported 与门控语义一个字都不变。
|
|
4103
4367
|
state.status === "ready" && state.data.compat
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4368
|
+
&& (state.data.compat.noticeVisible === undefined
|
|
4369
|
+
? !state.data.compat.supported
|
|
4370
|
+
: state.data.compat.noticeVisible === true)
|
|
4371
|
+
&& !compatNoticeClosed
|
|
4372
|
+
? el("p", { className: styles.message, "data-error": "true" },
|
|
4373
|
+
state.data.compat.notice,
|
|
4374
|
+
" ",
|
|
4375
|
+
el("button", {
|
|
4376
|
+
type: "button",
|
|
4377
|
+
style: { marginLeft: "8px", cursor: "pointer" },
|
|
4378
|
+
title: t("compatNoticeDismiss"),
|
|
4379
|
+
onClick: () => {
|
|
4380
|
+
// 失败不收起(关了却还在服务端显示会造成「点了没反应」的错觉)
|
|
4381
|
+
call("/plugin-console/compat-ack", { webAppVersion: state.data.compat?.webAppVersion })
|
|
4382
|
+
.then(() => setCompatNoticeClosed(true), () => {});
|
|
4383
|
+
},
|
|
4384
|
+
}, t("compatNoticeDismiss")),
|
|
4385
|
+
)
|
|
4386
|
+
: null,
|
|
4123
4387
|
state.status === "ready" && comps.length > 0 && details === null && repoInfo === null
|
|
4124
4388
|
? el("button", {
|
|
4125
4389
|
type: "button",
|
|
@@ -4311,7 +4575,7 @@ window.__ModuleLoader__.load({
|
|
|
4311
4575
|
el("p", { className: styles.message }, t("compatGateHint")),
|
|
4312
4576
|
el("p", { className: styles.status, "data-pending": gatePendingCount > 0 ? "true" : undefined },
|
|
4313
4577
|
gatePendingCount > 0 ? t("gatePendingInfo").replace("{n}", String(gatePendingCount)) : t("gatePendingNone")),
|
|
4314
|
-
|
|
4578
|
+
el("p", { className: styles.status, "data-pending": gatePendingCount > 0 ? "true" : undefined },
|
|
4315
4579
|
gatePendingCount > 0 ? t("gatePendingInfo").replace("{n}", String(gatePendingCount)) : t("gatePendingNone")),
|
|
4316
4580
|
// 明细清单(2026-09-24 用户要求:面板要能看到是哪些插件,有列表数据)
|
|
4317
4581
|
gateRows !== null && gateRows.pendingCount + gateRows.adoptedCount > 0
|
|
@@ -4465,49 +4729,49 @@ window.__ModuleLoader__.load({
|
|
|
4465
4729
|
);
|
|
4466
4730
|
},
|
|
4467
4731
|
}, "🧹 " + t("cleanResiduals")),
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4732
|
+
el("button", {
|
|
4733
|
+
type: "button",
|
|
4734
|
+
style: { position: "fixed", left: 12, bottom: 44, zIndex: 1000, opacity: 0.75 },
|
|
4735
|
+
title: t("cleanFrameworkResiduals"),
|
|
4736
|
+
onClick: () => {
|
|
4737
|
+
// 2026-09-28 加法:框架残留清理(顶层旧版本目录 / framework-backups)。两步走:
|
|
4738
|
+
// 先 dryRun 出报告 → window.confirm 让用户看清要动什么 → 才带 confirm:true 真执行。
|
|
4739
|
+
// 服务端默认 dryRun,所以「误点一下」不会动任何文件。
|
|
4740
|
+
const SCOPES = ["framework", "frameworkBackups", "pnpmEntities"];
|
|
4741
|
+
setMessage(t("cleanFrameworkResiduals") + "…");
|
|
4742
|
+
call("/plugin-console/clean-residuals", { scope: SCOPES }).then(
|
|
4743
|
+
(report) => {
|
|
4744
|
+
const fw = report && report.framework ? report.framework : null;
|
|
4745
|
+
const bk = report && report.frameworkBackups ? report.frameworkBackups : null;
|
|
4746
|
+
const pe = report && report.pnpmEntities ? report.pnpmEntities : null;
|
|
4747
|
+
const stale = fw && Array.isArray(fw.planned) ? fw.planned.length : 0;
|
|
4748
|
+
const pend = bk && Array.isArray(bk.plannedRemove) ? bk.plannedRemove : [];
|
|
4749
|
+
const mb = Math.round(pend.reduce((s, it) => s + (it && Number.isFinite(it.sizeBytes) ? it.sizeBytes : 0), 0) / 1048576);
|
|
4750
|
+
const entities = pe && Number.isFinite(pe.entityCount) ? pe.entityCount : 0;
|
|
4751
|
+
const head = t("cleanFrameworkResiduals") + ":" + t("cfStale").replace("{n}", String(stale))
|
|
4752
|
+
+ "、" + t("cfBackups").replace("{n}", String(pend.length)).replace("{mb}", String(mb))
|
|
4753
|
+
+ (entities > 0 ? ";" + t("cfEntities").replace("{n}", String(entities)) : "");
|
|
4754
|
+
if (stale === 0 && pend.length === 0) { setMessage(head + ";" + t("cfNothing")); return; }
|
|
4755
|
+
if (!window.confirm(head + "\n" + t("cfConfirm"))) { setMessage(head + ";" + t("cfCancelled")); return; }
|
|
4756
|
+
setMessage(t("cfRunning"));
|
|
4757
|
+
call("/plugin-console/clean-residuals", { scope: SCOPES, confirm: true, keep: 1 }).then(
|
|
4758
|
+
(done) => {
|
|
4759
|
+
const renamed = done && done.framework && Array.isArray(done.framework.renamed) ? done.framework.renamed.length : 0;
|
|
4760
|
+
const relinked = done && done.framework && Array.isArray(done.framework.relinked) ? done.framework.relinked.length : 0;
|
|
4761
|
+
const removed = done && done.frameworkBackups && Array.isArray(done.frameworkBackups.removed) ? done.frameworkBackups.removed.length : 0;
|
|
4762
|
+
const freed = done && done.frameworkBackups && Number.isFinite(done.frameworkBackups.freedBytes) ? Math.round(done.frameworkBackups.freedBytes / 1048576) : 0;
|
|
4763
|
+
const failed = (done && done.framework && Array.isArray(done.framework.failed) ? done.framework.failed.length : 0)
|
|
4764
|
+
+ (done && done.frameworkBackups && Array.isArray(done.frameworkBackups.failed) ? done.frameworkBackups.failed.length : 0);
|
|
4765
|
+
setMessage(t("cfDone").replace("{renamed}", String(renamed)).replace("{linked}", String(relinked)).replace("{removed}", String(removed)).replace("{mb}", String(freed))
|
|
4766
|
+
+ (failed > 0 ? ";" + t("cfFailed").replace("{n}", String(failed)) : ""));
|
|
4767
|
+
},
|
|
4768
|
+
(error) => setMessage(t("safetySwitchFailed") + ":" + friendlyGithubError(error).message),
|
|
4769
|
+
);
|
|
4770
|
+
},
|
|
4771
|
+
(error) => setMessage(t("safetySwitchFailed") + ":" + friendlyGithubError(error).message),
|
|
4772
|
+
);
|
|
4773
|
+
},
|
|
4774
|
+
}, "🧰 " + t("cleanFrameworkResiduals")),
|
|
4511
4775
|
fwShowCard
|
|
4512
4776
|
? el("div", { className: styles.detail },
|
|
4513
4777
|
el("div", { className: styles.rowTop },
|
|
@@ -4571,30 +4835,30 @@ window.__ModuleLoader__.load({
|
|
|
4571
4835
|
? t("marketTitle")
|
|
4572
4836
|
: t("marketTitleOther").replace("{source}", sourceDisplayName(searchSource)))),
|
|
4573
4837
|
el("div", { id: "pc-ghwrap", className: styles.ghwrap },
|
|
4574
|
-
selfUpdate !== null ? el("button", {
|
|
4575
|
-
type: "button",
|
|
4576
|
-
className: styles.ghpill,
|
|
4577
|
-
title: t("hubUpdateAvailable") + " v" + selfUpdate.latest,
|
|
4578
|
-
onClick: () => {
|
|
4579
|
-
setMessage(t("installing"));
|
|
4580
|
-
call("/plugin-console/self-update", {}).then(
|
|
4581
|
-
(data) => {
|
|
4582
|
-
if (data && data.ok === true) {
|
|
4583
|
-
// 未写进 lockfile 时必须**显眼**说清(用户实测报告:只铺文件不写 lock → 之后任何 pnpm 操作
|
|
4584
|
-
// 都会把版本还原,而用户以为升级成功了)
|
|
4585
|
-
const lockWarn = data.lockUpdated === false && typeof data.lockNote === "string" && data.lockNote !== ""
|
|
4586
|
-
? "⚠️ " + data.lockNote
|
|
4587
|
-
: "";
|
|
4588
|
-
setMessage((data.note ?? "已更新") + (lockWarn === "" ? "" : "。" + lockWarn) + ",正在重启生效…");
|
|
4589
|
-
restartServiceViaServer(setMessage, { reload: false });
|
|
4590
|
-
} else {
|
|
4591
|
-
setMessage(t("safetySwitchFailed") + ":" + (data?.reason ?? "未知"));
|
|
4592
|
-
}
|
|
4593
|
-
},
|
|
4594
|
-
(error) => setMessage(t("safetySwitchFailed") + ":" + friendlyGithubError(error).message),
|
|
4595
|
-
);
|
|
4596
|
-
}
|
|
4597
|
-
}, "⬇ " + t("hubUpdateAvailable") + " v" + selfUpdate.latest) : null,
|
|
4838
|
+
selfUpdate !== null ? el("button", {
|
|
4839
|
+
type: "button",
|
|
4840
|
+
className: styles.ghpill,
|
|
4841
|
+
title: t("hubUpdateAvailable") + " v" + selfUpdate.latest,
|
|
4842
|
+
onClick: () => {
|
|
4843
|
+
setMessage(t("installing"));
|
|
4844
|
+
call("/plugin-console/self-update", {}).then(
|
|
4845
|
+
(data) => {
|
|
4846
|
+
if (data && data.ok === true) {
|
|
4847
|
+
// 未写进 lockfile 时必须**显眼**说清(用户实测报告:只铺文件不写 lock → 之后任何 pnpm 操作
|
|
4848
|
+
// 都会把版本还原,而用户以为升级成功了)
|
|
4849
|
+
const lockWarn = data.lockUpdated === false && typeof data.lockNote === "string" && data.lockNote !== ""
|
|
4850
|
+
? "⚠️ " + data.lockNote
|
|
4851
|
+
: "";
|
|
4852
|
+
setMessage((data.note ?? "已更新") + (lockWarn === "" ? "" : "。" + lockWarn) + ",正在重启生效…");
|
|
4853
|
+
restartServiceViaServer(setMessage, { reload: false });
|
|
4854
|
+
} else {
|
|
4855
|
+
setMessage(t("safetySwitchFailed") + ":" + (data?.reason ?? "未知"));
|
|
4856
|
+
}
|
|
4857
|
+
},
|
|
4858
|
+
(error) => setMessage(t("safetySwitchFailed") + ":" + friendlyGithubError(error).message),
|
|
4859
|
+
);
|
|
4860
|
+
}
|
|
4861
|
+
}, "⬇ " + t("hubUpdateAvailable") + " v" + selfUpdate.latest) : null,
|
|
4598
4862
|
el("button", {
|
|
4599
4863
|
type: "button",
|
|
4600
4864
|
className: styles.ghpill,
|
|
@@ -4922,7 +5186,25 @@ setMessage(t("safetySwitchFailed") + ":" + (data?.reason ?? "未知"));
|
|
|
4922
5186
|
el("button", { type: "button", onClick: search }, t("search")),
|
|
4923
5187
|
),
|
|
4924
5188
|
!marketCollapsed && marketRows.length > 0 ? el("ul", { className: styles.market }, marketRows) : null,
|
|
4925
|
-
|
|
5189
|
+
// 0.5.35:结果状态行 —— "共 N 条 / 已显示 M 条"(数据源没给总数时只报"已显示 M 条",
|
|
5190
|
+
// 绝不编造总数);**被截断**时明说"可继续加载"(而不是让用户以为"就是没有")。
|
|
5191
|
+
!marketCollapsed && market !== null && market.status === "ready" && market.data.length > 0
|
|
5192
|
+
? el("p", { className: styles.note, "data-truncated": market.hints !== null && market.hints !== undefined && market.hints.truncated === true ? "true" : "false" },
|
|
5193
|
+
(market.hints !== null && market.hints !== undefined && typeof market.hints.total === "number"
|
|
5194
|
+
? t("marketCount").replace("{total}", String(market.hints.total)).replace("{shown}", String(market.data.length))
|
|
5195
|
+
: t("marketCountKnown").replace("{shown}", String(market.data.length)))
|
|
5196
|
+
+ (market.hints !== null && market.hints !== undefined && market.hints.truncated === true
|
|
5197
|
+
? " · " + (market.hints.hasMore === true ? t("marketTruncated") : t("marketTruncatedEnd"))
|
|
5198
|
+
+ (typeof market.hints.reason === "string" && market.hints.reason !== "" ? ":" + market.hints.reason : "")
|
|
5199
|
+
: ""))
|
|
5200
|
+
: null,
|
|
5201
|
+
// 「加载更多」的判据从"长度 ≥ 20"改成**数据源给的口径**(hints.hasMore):
|
|
5202
|
+
// 旧判据下 3 条命中就不渲染按钮(用户"无从往下看"),而满 20 条又会在到底之后继续给空按钮。
|
|
5203
|
+
// hints 缺失(老响应/本地索引以外的场景)时退回旧判据,不假装知道。
|
|
5204
|
+
!marketCollapsed && market !== null && market.status === "ready"
|
|
5205
|
+
&& (market.hints !== null && market.hints !== undefined
|
|
5206
|
+
? market.hints.hasMore === true
|
|
5207
|
+
: market.data.length >= 20)
|
|
4926
5208
|
? el("div", { className: styles.descTopbar },
|
|
4927
5209
|
el("button", {
|
|
4928
5210
|
type: "button",
|
|
@@ -5359,11 +5641,11 @@ setMessage(t("safetySwitchFailed") + ":" + (data?.reason ?? "未知"));
|
|
|
5359
5641
|
// 所以做法是「就地抽屉」,而不是往别人的 DOM 里注入(注入会在对方更新时碎掉)。
|
|
5360
5642
|
// 原 tab(id=console)与悬浮面板一行未动。
|
|
5361
5643
|
/** 「建议动作」执行框(2026-09-27 加法,用户明确要求):安装结果旁的一个**小**框 ——
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5644
|
+
* 一句短说明 + 等宽只读命令 + 「执行」「复制」+ 执行中状态 + 结果/错误回显 + 可关闭。
|
|
5645
|
+
* 边界(与服务端同一条):面板只发**结构化动作**(`suggestedAction.payload`),
|
|
5646
|
+
* 命令字符串仅用于展示/复制;服务端只认白名单动作类型(见 domain/plugin-actions.js)。
|
|
5647
|
+
* 只用既有 styles.*(detail / rowTop / phase / name / message / note / toggle / trashBtn),
|
|
5648
|
+
* 不新增徽章、不新增独立面板 —— 长解释一律进悬浮 title。 */
|
|
5367
5649
|
function SuggestedActionBox({ job, t }) {
|
|
5368
5650
|
const [state, setState] = react.useState({ phase: "idle", text: null, level: null, title: null, closed: false });
|
|
5369
5651
|
// 2026-09-27 加法(选项 2):第二个显式动作的状态(「允许这些构建脚本」)。
|
|
@@ -5504,7 +5786,7 @@ setMessage(t("safetySwitchFailed") + ":" + (data?.reason ?? "未知"));
|
|
|
5504
5786
|
title: overwrite.title === null ? undefined : overwrite.title,
|
|
5505
5787
|
}, overwrite.text)));
|
|
5506
5788
|
}
|
|
5507
|
-
|
|
5789
|
+
|
|
5508
5790
|
function PluginSafetyTab({ t }) {
|
|
5509
5791
|
const [open, setOpen] = react.useState(false);
|
|
5510
5792
|
const [status, setStatus] = react.useState(null);
|
|
@@ -5738,4 +6020,4 @@ setMessage(t("safetySwitchFailed") + ":" + (data?.reason ?? "未知"));
|
|
|
5738
6020
|
exports.canAdaptUnlock = canAdaptUnlock;
|
|
5739
6021
|
return module.exports;
|
|
5740
6022
|
}
|
|
5741
|
-
});
|
|
6023
|
+
});
|