@noob-stupid/dsh-plugin-console 0.3.52 → 0.3.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -308,7 +308,7 @@ MIT
308
308
 
309
309
  The layered-refactor preview lives in its **own repository**: [**`Noob-stupid/dsh-plugin-hub-refactor`**](https://github.com/Noob-stupid/dsh-plugin-hub-refactor)
310
310
 
311
- - **What it is**: the monolithic `lib/index.js` (7637 lines) split into layers — `lib/index.js` **142 lines** + `lib/server/**` **36 modules**; **feature-equivalent to `0.3.52`**, maintainability only;
312
- - **Verified**: 18 test suites green · 8 architecture-guard assertions · **47/47 routes identical** (`status` + response fields) against the stable build;
313
- - **Not yet exercised**: real framework upgrade / rollback, restart guardian, real install/uninstall, component start/stop, real AI run, Gitee OAuth (~10–25% long-tail risk, subjective);
314
- - **For everyday use** stick to npm `@noob-stupid/dsh-plugin-console@0.3.52`, or `main` of this repo.
311
+ - **What it is**: the monolithic `lib/index.js` (8547 lines) split into layers — `lib/index.js` **141 lines** + `lib/server/**` **38 modules**; **feature-equivalent to `0.3.54`**, maintainability only;
312
+ - **Verified**: 19 test suites green · 8 architecture-guard assertions · **route inventory identical** (`status` + response fields) against the stable build;
313
+ - **Not yet exercised**: real framework upgrade / rollback, restart guardian, component start/stop, real AI run, Gitee OAuth (~10–25% long-tail risk, subjective);
314
+ - **For everyday use** stick to npm `@noob-stupid/dsh-plugin-console@0.3.54`, or `main` of this repo.
package/README.zh.md CHANGED
@@ -275,8 +275,9 @@ MIT
275
275
 
276
276
  分层重构预览线在**独立仓库**:[**`Noob-stupid/dsh-plugin-hub-refactor`**](https://github.com/Noob-stupid/dsh-plugin-hub-refactor)
277
277
 
278
- - **它是什么**:把单体 `lib/index.js`(7637 行)拆成分层结构 —— `lib/index.js` **142 行** + `lib/server/**` **36 个模块**,**功能与 `0.3.52` 等价**,只为可维护性;
279
- - **已验证**:18 套测试全绿 · 8 条架构守卫断言 · 与稳定版逐条对打 **47 条路由,`status` + 响应字段 47/47 一致**;
280
- - **尚未实测**:真框架升级 / 真回滚、重启守护链路、真装真卸、组件进程启停、AI 真跑、Gitee OAuth 回调(长尾风险主观估计 **10%~25%**);
281
- - **日常使用请继续用**:npm `@noob-stupid/dsh-plugin-console@0.3.52`,或本仓库 `main`。
278
+ - **它是什么**:把单体 `lib/index.js`(8547 行)拆成分层结构 —— `lib/index.js` **141 行** + `lib/server/**` **38 个模块**,**功能与 `0.3.54` 等价**,只为可维护性;
279
+ - **已验证**:19 套测试全绿 · 8 条架构守卫断言 · 与稳定版逐条对打**路由清单一致**(`status` + 响应字段);
280
+ - **已实测**(2026-09-20 真装真卸演练):普通插件 / bundle 插件 / 无 npm 仓库 / 套装 / 技能 / 聚合仓库子包 / 仓库落地 / 服务器组件启停;
281
+ - **尚未实测**:真框架升级 / 真回滚、重启守护链路、AI 真跑、Gitee OAuth 回调(长尾风险主观估计 **10%~25%**);
282
+ - **日常使用请继续用**:npm `@noob-stupid/dsh-plugin-console@0.3.54`,或本仓库 `main`。
282
283
  - 请多反馈问题
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:20px;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:56px;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:104px}.pc_toolbarGroup{top:140px}.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)}";
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:20px;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:56px;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:104px}.pc_toolbarGroup{top:140px}.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");
@@ -64,6 +64,10 @@ window.__ModuleLoader__.load({
64
64
  modeFloat: "pc_modeFloat",
65
65
  trashBtn: "pc_trashBtn",
66
66
  consentRemember: "pc_consentRemember",
67
+ consentCard: "pc_consentCard",
68
+ consentCountdown: "pc_consentCountdown",
69
+ consentApprove: "pc_consentApprove",
70
+ progress: "pc_progress",
67
71
  modalBackdrop: "pc_modalBackdrop",
68
72
  modalCard: "pc_modalCard",
69
73
  ghwrap: "pc_ghwrap",
@@ -203,6 +207,10 @@ window.__ModuleLoader__.load({
203
207
  loadingDetails: "正在读取详情…",
204
208
  githubLoggedIn: "已登录 GitHub:",
205
209
  githubCornerOut: "未登录 GitHub",
210
+ githubLoginViaApp: "GitHub 登录",
211
+ githubLoginOpened: "已打开 GitHub 授权窗口,请在页面上输入验证码完成授权…",
212
+ githubLoginUnavailable: "无法使用窗口登录",
213
+ githubLoginOrToken: "或用 Token 登录",
206
214
  githubLoginTip: "粘贴 GitHub Token 登录(fine-grained,需 read:user 权限)。登录后可用代码搜索(按子包名搜索)并提高 API 限额;也可用 dsh-github-login 插件登录。",
207
215
  githubTokenPlaceholder: "粘贴 token(ghp_… 或 github_pat_…)",
208
216
  githubLoginBtn: "登录",
@@ -238,10 +246,24 @@ window.__ModuleLoader__.load({
238
246
  deleting: "删除中…",
239
247
  deleteNote: "已删除插件并卸载包,正在刷新",
240
248
  deleteBundleNote: "已移除插件所属 bundle,重启服务后生效",
249
+ pendingRestartTag: "已安装·重启后生效",
250
+ pendingRestartHint: "包已装好,重启服务后才会出现在上面的插件列表里;现在删除可直接撤回这次安装",
251
+ pendingRestartDelete: "撤销这次安装(删补丁行 / bundles 清单 / 包目录)",
252
+ pendingRestartNote: "已撤销这次安装(补丁行、bundles 清单、包目录均已清理)",
253
+ pendingRestartPartial: "撤销未完全生效,请按提示手动处理",
241
254
  aiConsentText: "常规安装通道均已尝试失败。下一步将让本地 AI 接管安装——这会调用 DeepSeek API 模型,可能产生 API 费用。是否继续?",
242
255
  aiConsentNotifyTitle: "插件安装需要授权",
243
256
  aiConsentApprove: "同意,继续(可能产生费用)",
244
257
  aiConsentDecline: "取消",
258
+ aiConsentCardTitle: "⚠ 需要你的授权:本地 AI 兜底",
259
+ aiConsentCountdown: "剩余",
260
+ aiConsentTimeoutNote: "超过 {min} 分钟不做选择将自动取消(不会产生任何费用)",
261
+ aiConsentLastError: "确定性通道最后一个错误",
262
+ aiConsentTopHint: "⚠ 有安装任务在等你授权:确定性安装通道都失败了,可使用本地 AI 兜底(会调用模型 API 产生费用)——请在安装进度处选择「同意 / 取消」",
263
+ progressSubpackage: "正在装第 {index}/{total} 个子包:{name}",
264
+ progressSuiteClone: "正在拉取第 {index}/{total} 个子模块:{name}",
265
+ progressSuiteAssemble: "正在装配第 {index}/{total} 个子模块:{name}",
266
+ progressDone: "已完成(共 {total} 个)",
245
267
  aiFallbackLabel: "AI 兜底",
246
268
  aiEmpowerLabel: "AI 赋能",
247
269
  aiEmpowerTitle: "AI 赋能:读文档自动规划并部署组件;识别到服务器类组件后自动加启动/停止按钮",
@@ -584,6 +606,10 @@ window.__ModuleLoader__.load({
584
606
  hubUpdateAvailable: "Download update",
585
607
  cleanResiduals: "Clean residual backups",
586
608
  githubCornerOut: "Not signed in to GitHub",
609
+ githubLoginViaApp: "GitHub login",
610
+ githubLoginOpened: "Authorization window opened — enter the code on the GitHub page to finish",
611
+ githubLoginUnavailable: "Window login unavailable",
612
+ githubLoginOrToken: "Or sign in with a token",
587
613
  githubLoginTip: "Paste a GitHub token to sign in (fine-grained, needs the read:user scope). Signing in enables code search (search by subpackage name) and raises API rate limits; you can also sign in with the dsh-github-login plugin.",
588
614
  githubTokenPlaceholder: "Paste token (ghp_… or github_pat_…)",
589
615
  githubLoginBtn: "Sign in",
@@ -617,10 +643,24 @@ window.__ModuleLoader__.load({
617
643
  deleting: "Deleting…",
618
644
  deleteNote: "Plugin deleted and package uninstalled; refreshing",
619
645
  deleteBundleNote: "Owning bundle removed; takes effect after service restart",
646
+ pendingRestartTag: "Installed · after restart",
647
+ pendingRestartHint: "The package is installed and will show up in the list above after a restart; deleting now revokes this install",
648
+ pendingRestartDelete: "Revoke this install (drop patch row, bundles entry and package dir)",
649
+ pendingRestartNote: "Install revoked (patch row, bundles entry and package dir cleaned)",
650
+ pendingRestartPartial: "Revoke incomplete — handle the leftovers manually as reported",
620
651
  aiConsentText: "All regular install channels failed. Next, the local AI takes over the install, which calls a DeepSeek API model and may incur API costs. Continue?",
621
652
  aiConsentNotifyTitle: "Plugin install needs approval",
622
653
  aiConsentApprove: "Agree, continue (may incur costs)",
623
654
  aiConsentDecline: "Cancel",
655
+ aiConsentCardTitle: "⚠ Approval needed: local AI fallback",
656
+ aiConsentCountdown: "Time left",
657
+ aiConsentTimeoutNote: "No choice within {min} minutes cancels it automatically (no cost at all)",
658
+ aiConsentLastError: "Last error from the deterministic channels",
659
+ aiConsentTopHint: "⚠ An install is waiting for your approval: every deterministic channel failed, and the local AI fallback (which calls the model API and may incur costs) needs your consent — choose Agree / Cancel in the install progress area",
660
+ progressSubpackage: "Installing subpackage {index}/{total}: {name}",
661
+ progressSuiteClone: "Cloning submodule {index}/{total}: {name}",
662
+ progressSuiteAssemble: "Assembling submodule {index}/{total}: {name}",
663
+ progressDone: "Completed ({total} total)",
624
664
  aiFallbackLabel: "AI fallback",
625
665
  aiEmpowerLabel: "AI Empower",
626
666
  aiEmpowerTitle: "AI Empower: read docs, plan and deploy a component automatically; server-type components get start/stop buttons",
@@ -1906,16 +1946,6 @@ window.__ModuleLoader__.load({
1906
1946
  document.addEventListener("mousedown", onDown);
1907
1947
  return () => document.removeEventListener("mousedown", onDown);
1908
1948
  }, [sourceMenuOpen]);
1909
- // GitHub 登录面板:点击外部收起(与搜索源菜单同一模式)
1910
- react.useEffect(() => {
1911
- if (!ghLoginOpen) return;
1912
- const onDown = (event) => {
1913
- const area = document.getElementById("pc-ghlogin");
1914
- if (area && !area.contains(event.target)) setGhLoginOpen(false);
1915
- };
1916
- document.addEventListener("mousedown", onDown);
1917
- return () => document.removeEventListener("mousedown", onDown);
1918
- }, [ghLoginOpen]);
1919
1949
  // 已安装搜索:点击搜索区以外且无内容时立即收起(无延迟)
1920
1950
  react.useEffect(() => {
1921
1951
  if (!installedSearchOpen) return;
@@ -1943,6 +1973,69 @@ window.__ModuleLoader__.load({
1943
1973
  );
1944
1974
  }, []);
1945
1975
  react.useEffect(() => { refresh(); }, [refresh]);
1976
+ // ── GitHub 登录态的两个读数:面板里多处要用(徽章文案、菜单项、内联子面板)──
1977
+ const ghAuthed = state.status === "ready" && state.data.github !== undefined && state.data.github.loggedIn === true;
1978
+ const ghLoginName = state.status === "ready" && state.data.github !== undefined && state.data.github.login !== null && state.data.github.login !== undefined
1979
+ ? state.data.github.login
1980
+ : "unknown";
1981
+ // 设备码登录的轮询句柄:拿到登录态就停,用户手动收起菜单也停(不留后台空转的定时器)
1982
+ const ghPollRef = react.useRef(null);
1983
+ const stopGhPoll = react.useCallback(() => {
1984
+ if (ghPollRef.current !== null) { window.clearInterval(ghPollRef.current); ghPollRef.current = null; }
1985
+ }, []);
1986
+ // 组件卸载时收尾(本面板是长驻 UI,正常不卸载,但热重载/关闭面板会)
1987
+ react.useEffect(() => () => stopGhPoll(), [stopGhPoll]);
1988
+ // 菜单收起 = 用户手动关掉了这次登录尝试 → 立刻停止轮询(对应"最多 60s 或用户手动关闭")
1989
+ react.useEffect(() => { if (!sourceMenuOpen) stopGhPoll(); }, [sourceMenuOpen, stopGhPoll]);
1990
+ // 窗口登录开始后等登录态:每 2s 问一次 /state,最多 60s。
1991
+ // 为什么只能轮询:设备码流程在**另一个进程**里完成(用户在 GitHub 页面上输验证码授权),
1992
+ // 本插件收不到任何回调,只能盯 <DSH_HOME>/github-auth.json(/state 的 github 字段)有没有变。
1993
+ const startGhPoll = () => {
1994
+ stopGhPoll();
1995
+ const deadline = Date.now() + 60000;
1996
+ ghPollRef.current = window.setInterval(() => {
1997
+ call("/plugin-console/state").then(
1998
+ (snap) => {
1999
+ const github = snap !== null && snap !== undefined ? snap.github : null;
2000
+ if (github !== null && github !== undefined && github.loggedIn === true) {
2001
+ stopGhPoll();
2002
+ setMessage(t("githubLoginOk").replace("{login}", github.login ?? ""));
2003
+ refresh();
2004
+ return;
2005
+ }
2006
+ if (Date.now() >= deadline) stopGhPoll();
2007
+ },
2008
+ // 单次失败不中断:授权期间用户可能正在重启服务,下一拍再问
2009
+ () => { if (Date.now() >= deadline) stopGhPoll(); },
2010
+ );
2011
+ }, 2000);
2012
+ };
2013
+ // 菜单里的主入口「GitHub 登录」:首选设备码窗口流程(在 GitHub 页面上输验证码),
2014
+ // 通道不可用才降级到 token 粘贴子面板(token 是兜底,不是正路)。
2015
+ const githubOpenLogin = () => {
2016
+ if (ghLoginBusy) return;
2017
+ setGhLoginBusy(true);
2018
+ // 先给一句反馈:环回调用最长要等 8s,中间没有提示会让人以为点了没反应
2019
+ setMessage(t("githubLoginOpened"));
2020
+ call("/plugin-console/github-open-login", {}).then(
2021
+ (r) => {
2022
+ setGhLoginBusy(false);
2023
+ if (r !== null && r !== undefined && r.started === true) {
2024
+ setMessage(t("githubLoginOpened"));
2025
+ startGhPoll();
2026
+ return;
2027
+ }
2028
+ // 服务端回 started:false 时一定带 reason(插件没装/没有 exe/平台不支持),照念给用户
2029
+ setMessage(t("githubLoginUnavailable") + ":" + (r !== null && r !== undefined && typeof r.reason === "string" ? r.reason : ""));
2030
+ setGhLoginOpen(true);
2031
+ },
2032
+ (error) => {
2033
+ setGhLoginBusy(false);
2034
+ setMessage(t("githubLoginFail") + ":" + (error?.message ?? ""));
2035
+ setGhLoginOpen(true);
2036
+ },
2037
+ );
2038
+ };
1946
2039
  // 粘贴 token 登录:成功后清空输入 + 收起面板 + 刷新登录态(让徽章/代码搜索立刻用上新身份);
1947
2040
  // 失败**不清空输入**——用户多半是复制时多带了空格/少了一段,保留原文才好改。
1948
2041
  const githubLogin = () => {
@@ -2369,6 +2462,12 @@ window.__ModuleLoader__.load({
2369
2462
  call("/plugin-console/install-status", { jobId }).then(
2370
2463
  (data) => {
2371
2464
  setJobs((prev) => ({ ...prev, [jobId]: data }));
2465
+ // 授权请求必须"看得见"(2026-09-20 真装实测事故:面板只把 stage 变成
2466
+ // ai-consent,用户不知道要做什么 → 干等 10 分钟被超时取消)。顶部消息先说一句,
2467
+ // 详情卡在下面的安装进度区(同一位置的醒目卡片 + 倒计时 + 同意/取消)。
2468
+ if (data.status === "installing" && data.stage === "ai-consent" && data.aiConsent?.pending === true && !aiAutoDeclinedRef.current[jobId]) {
2469
+ setMessage(t("aiConsentTopHint"));
2470
+ }
2372
2471
  // 需要用户授权时(面板页后台/失焦也能看到):系统通知 + 标题闪烁提示
2373
2472
  if (data.status === "installing" && data.stage === "ai-consent" && aiFallback && !aiRemember && !aiAutoDeclinedRef.current[jobId]) {
2374
2473
  aiAutoDeclinedRef.current[jobId] = true;
@@ -2453,6 +2552,33 @@ window.__ModuleLoader__.load({
2453
2552
  (error) => setMessage(t("failed") + ":" + friendlyGithubError(error).message),
2454
2553
  );
2455
2554
  };
2555
+ /** 安装进度文案:服务端 progress{channel,phase,index,total,name,done} → 一行人话。
2556
+ * 2026-09-20 真装实测:11 个子包的聚合仓库跑 19 分钟,面板只有"安装中"——用户不知道
2557
+ * 在第几个、还剩几个、当前装的是谁。这里把服务端下发的进度翻成"第 i/n 个:<名字>"。 */
2558
+ const progressText = (progress) => {
2559
+ if (progress === null || progress === undefined) return "";
2560
+ if (progress.done === true) return t("progressDone").replace("{total}", String(progress.total ?? 0));
2561
+ const key = progress.channel === "suite"
2562
+ ? (progress.phase === "clone" ? "progressSuiteClone" : "progressSuiteAssemble")
2563
+ : "progressSubpackage";
2564
+ return t(key)
2565
+ .replace("{index}", String(progress.index ?? 0))
2566
+ .replace("{total}", String(progress.total ?? 0))
2567
+ .replace("{name}", progress.name ?? "");
2568
+ };
2569
+ /** 授权倒计时(剩余 mm:ss):服务端只给请求时间 + 超时上限,剩余量在前端算。
2570
+ * 轮询每 2s 触发一次重渲染,倒计时随之刷新,不需要额外的定时器。 */
2571
+ const consentRemaining = (job) => {
2572
+ const consent = job?.aiConsent ?? null;
2573
+ if (consent === null || consent.pending !== true || typeof consent.since !== "number") return null;
2574
+ const timeoutMs = typeof consent.timeoutMs === "number" && consent.timeoutMs > 0 ? consent.timeoutMs : 600000;
2575
+ const left = Math.max(0, Math.round((consent.since + timeoutMs - Date.now()) / 1000));
2576
+ return Math.floor(left / 60) + ":" + String(left % 60).padStart(2, "0");
2577
+ };
2578
+ const consentTimeoutMinutes = (job) => {
2579
+ const timeoutMs = job?.aiConsent?.timeoutMs;
2580
+ return String(Math.max(1, Math.round((typeof timeoutMs === "number" && timeoutMs > 0 ? timeoutMs : 600000) / 60000)));
2581
+ };
2456
2582
  /** 软件源管理:打开时拉取当前配置。 */
2457
2583
  const openSources = () => {
2458
2584
  setSourcesOpen(true);
@@ -3060,7 +3186,62 @@ window.__ModuleLoader__.load({
3060
3186
  const root = section.head !== null ? section.head.key.slice("family-".length) : null;
3061
3187
  if (root === null || familyCollapsed[root] !== true) familySectionEls.push(...section.cards);
3062
3188
  }
3063
- const rows = state.status === "ready" ? [...nonFamilyCards, ...familySectionEls] : [];
3189
+ // 已安装但尚未生效(bundle 型要重启才被加载;对应服务端 /state 的 pendingRestart):
3190
+ // 演练实测(2026-09-20)装完这类插件时 entryId 为 null、loader 里也没有新条目 ——
3191
+ // 用户以为没装上,删除按钮也无处可点。这里渲染成同样的行 + 「已安装·重启后生效」徽标,
3192
+ // 行上的删除按钮按 jobId 撤销这次安装(服务端删补丁行 / bundles 清单 / 包目录)。
3193
+ const pendingCards = (state.status === "ready" ? state.data.pendingRestart ?? [] : [])
3194
+ .filter((job) => installedQueryNorm === "" || [job.packageName, job.repo, job.jobId]
3195
+ .some((value) => String(value ?? "").toLowerCase().includes(installedQueryNorm)))
3196
+ .map((job) => el("li", { key: "pending-" + job.jobId, className: styles.row },
3197
+ el("div", { className: styles.rowTop },
3198
+ el("strong", { className: styles.name, title: job.packageName }, job.packageName),
3199
+ el("span", { className: styles.tag, "data-pending": "true", title: t("pendingRestartHint") }, t("pendingRestartTag")),
3200
+ ),
3201
+ el("div", { className: styles.meta },
3202
+ el("code", null, job.jobId),
3203
+ job.repo ? el("code", null, job.repo) : null,
3204
+ ),
3205
+ el("div", { className: styles.rowTop, style: { justifyContent: "space-between" } },
3206
+ el("p", { className: styles.desc }, t("pendingRestartHint")),
3207
+ el("div", { className: styles.rowTop },
3208
+ el("button", {
3209
+ type: "button",
3210
+ className: styles.trashBtn,
3211
+ title: t("pendingRestartDelete"),
3212
+ "aria-label": t("pendingRestartDelete"),
3213
+ disabled: deleteBusy === job.jobId,
3214
+ onClick: () => {
3215
+ if (confirmDeleteRowId === job.jobId) {
3216
+ setDeleteBusy(job.jobId);
3217
+ call("/plugin-console/uninstall", { jobId: job.jobId }).then(
3218
+ (data) => {
3219
+ setConfirmDeleteRowId(null);
3220
+ setMessage(data && data.warn
3221
+ ? t("pendingRestartPartial") + ":" + data.warn
3222
+ : t("pendingRestartNote") + ":" + (data?.packageName ?? "") + (data?.uninstallError ? "(包卸载警告:" + data.uninstallError + ")" : ""));
3223
+ refresh();
3224
+ },
3225
+ (error) => setMessage(t("failed") + ":" + friendlyGithubError(error).message),
3226
+ ).finally(() => setDeleteBusy(null));
3227
+ } else {
3228
+ setConfirmDeleteRowId(job.jobId);
3229
+ window.setTimeout(() => setConfirmDeleteRowId((v) => (v === job.jobId ? null : v)), 3000);
3230
+ }
3231
+ },
3232
+ }, deleteBusy === job.jobId
3233
+ ? t("deleting")
3234
+ : confirmDeleteRowId === job.jobId
3235
+ ? t("confirmDelete")
3236
+ : el("svg", { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" },
3237
+ el("polyline", { points: "3 6 5 6 21 6" }),
3238
+ el("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }),
3239
+ el("line", { x1: "10", y1: "11", x2: "10", y2: "17" }),
3240
+ el("line", { x1: "14", y1: "11", x2: "14", y2: "17" }))),
3241
+ ),
3242
+ ),
3243
+ ));
3244
+ const rows = state.status === "ready" ? [...nonFamilyCards, ...familySectionEls, ...pendingCards] : [];
3064
3245
  const marketShown = market === null || market.status !== "ready"
3065
3246
  ? []
3066
3247
  : officialOnly
@@ -3683,14 +3864,19 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
3683
3864
  el("button", {
3684
3865
  type: "button",
3685
3866
  className: styles.ghpill,
3867
+ // 外观回退:市场页只有**这一颗** pill,登录态体现在它的绿色描边上(data-on),
3868
+ // 不再单独占一颗登录徽章
3869
+ "data-on": ghAuthed ? "true" : "false",
3686
3870
  title: t("searchSourceTitle"),
3687
3871
  "aria-label": t("searchSourceTitle"),
3688
3872
  onClick: () => setSourceMenuOpen((v) => !v),
3689
3873
  },
3690
- // 这一颗是「搜索源」选择器:只报当前源名。登录态搬去右边的 GitHub 徽章
3691
- // (原来它顺手显示登录态,导致"未登录 GitHub"看着像徽章却点开的是源菜单)
3692
- t("sourceOf") + ((searchSourcesList !== null ? searchSourcesList.find((s) => s.id === searchSource)?.name : undefined)
3693
- ?? (searchSource === "gitee" ? "Gitee" : searchSource)),
3874
+ // 源选在 GitHub 时这颗 pill 顺手报登录态(回到上一批之前的形态);
3875
+ // 选别的源时只报源名 —— 否则"未登录 GitHub"看着像徽章却点开的是源菜单
3876
+ searchSource === "github"
3877
+ ? (ghAuthed ? t("githubLoggedIn") + ghLoginName : t("githubCornerOut"))
3878
+ : t("sourceOf") + ((searchSourcesList !== null ? searchSourcesList.find((s) => s.id === searchSource)?.name : undefined)
3879
+ ?? (searchSource === "gitee" ? "Gitee" : searchSource)),
3694
3880
  " ▾"),
3695
3881
  sourceMenuOpen
3696
3882
  ? el("div", { className: styles.sourceMenu },
@@ -3708,37 +3894,47 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
3708
3894
  try { localStorage.setItem("pc-search-source", s.id); } catch {}
3709
3895
  setSourceMenuOpen(false);
3710
3896
  },
3711
- }, s.name)))
3897
+ }, s.name)),
3898
+ // ── 登录区(登录入口收进这颗 pill 的菜单里,不再单独一颗徽章)──
3899
+ el("div", { style: { height: 1, margin: "4px 2px", background: "var(--dsw-alias-border-l2)" } }),
3900
+ ghAuthed
3901
+ // 已登录:这行只报状态(点了把状态念到消息栏,不做别的)
3902
+ ? el("button", {
3903
+ type: "button",
3904
+ className: styles.sourceOpt,
3905
+ "data-active": "true",
3906
+ title: t("githubLoginTip"),
3907
+ onClick: () => setMessage(t("githubLoggedIn") + ghLoginName),
3908
+ }, t("githubLoggedIn") + ghLoginName)
3909
+ // 未登录:主入口走设备码窗口登录;窗口不可用时它自己会展开下面的 token 子面板
3910
+ : el("button", {
3911
+ type: "button",
3912
+ className: styles.sourceOpt,
3913
+ disabled: ghLoginBusy,
3914
+ title: t("githubLoginTip"),
3915
+ onClick: githubOpenLogin,
3916
+ }, t("githubLoginViaApp")),
3917
+ // 次要入口:token 兜底(已登录时也留着 —— 换账号/换 token 就靠它)
3918
+ el("button", {
3919
+ type: "button",
3920
+ className: styles.sourceOpt,
3921
+ onClick: () => setGhLoginOpen((v) => !v),
3922
+ }, t("githubLoginOrToken")),
3923
+ // token 兜底子面板:窗口登录不可用(插件没装/非 Windows/没有 exe)时自动展开;
3924
+ // 也可以从上面「或用 Token 登录」主动开合。样式内联,不为它改 CSS 表。
3925
+ ghLoginOpen
3926
+ ? el("div", { style: { display: "flex", flexDirection: "column", gap: 6, width: 280, boxSizing: "border-box", padding: "2px 2px 4px" } },
3927
+ el("p", { className: styles.message, style: { margin: 0 } }, t("githubLoginTip")),
3928
+ el("input", {
3929
+ type: "password",
3930
+ placeholder: t("githubTokenPlaceholder"),
3931
+ value: ghToken,
3932
+ onChange: (event) => setGhToken(event.currentTarget.value),
3933
+ style: { width: "100%", boxSizing: "border-box" },
3934
+ }),
3935
+ el("button", { type: "button", className: styles.toggle, disabled: ghLoginBusy || ghToken.trim() === "", onClick: githubLogin }, t("githubLoginBtn")))
3936
+ : null)
3712
3937
  : null,
3713
- // 「未登录 GitHub / 已登录 GitHub:xxx」徽章:原来只是展示(点了没反应),
3714
- // 现在点开内联登录面板(粘贴 PAT 即登录),面板样式内联,避免为它改 CSS 表。
3715
- el("div", { id: "pc-ghlogin", style: { position: "relative", display: "inline-flex" } },
3716
- el("button", {
3717
- type: "button",
3718
- className: styles.ghpill,
3719
- "data-on": state.status === "ready" && state.data.github && state.data.github.loggedIn ? "true" : "false",
3720
- title: t("githubLoginTip"),
3721
- "aria-label": t("githubLoginTip"),
3722
- onClick: () => setGhLoginOpen((v) => !v),
3723
- },
3724
- state.status === "ready" && state.data.github && state.data.github.loggedIn
3725
- ? t("githubLoggedIn") + (state.data.github.login ?? "unknown")
3726
- : t("githubCornerOut")),
3727
- ghLoginOpen
3728
- ? el("div", { style: { position: "absolute", right: 0, top: "calc(100% + 6px)", zIndex: 95, width: 300, boxSizing: "border-box", border: "1px solid var(--dsw-alias-border-l2)", background: "var(--dsw-alias-bg-layer-1)", borderRadius: 8, padding: 10, display: "flex", flexDirection: "column", gap: 8, textAlign: "left", boxShadow: "0 8px 24px rgba(0,0,0,.18)" } },
3729
- el("p", { className: styles.message, style: { margin: 0 } }, t("githubLoginTip")),
3730
- state.status === "ready" && state.data.github && state.data.github.loggedIn
3731
- ? el("p", { className: styles.message, style: { margin: 0 } }, t("githubLoggedIn") + (state.data.github.login ?? "unknown"))
3732
- : null,
3733
- el("input", {
3734
- type: "password",
3735
- placeholder: t("githubTokenPlaceholder"),
3736
- value: ghToken,
3737
- onChange: (event) => setGhToken(event.currentTarget.value),
3738
- style: { width: "100%", boxSizing: "border-box" },
3739
- }),
3740
- el("button", { type: "button", className: styles.toggle, disabled: ghLoginBusy || ghToken.trim() === "", onClick: githubLogin }, t("githubLoginBtn")))
3741
- : null),
3742
3938
  )),
3743
3939
  (aiRunning || aiJobsList.some((x) => x.status === "running"))
3744
3940
  ? el("button", {
@@ -4003,21 +4199,36 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
4003
4199
  : null,
4004
4200
  Object.values(jobs).filter((job) => job.status === "installing").map((job) => {
4005
4201
  if (job.stage === "ai-consent") {
4006
- // 需要用户同意时由最上层模态框负责展示,列表内只留状态行;
4007
- // 已关闭兜底或已勾选"不再提醒"时显示对应说明
4008
- const autoNote = !aiFallback ? t("aiFallbackOff") : (aiRemember ? t("aiRememberNote") : t("consentWaiting"));
4009
- return el("div", { key: job.jobId, className: styles.detail },
4202
+ // 授权请求就摆在**安装进度所在位置**(用户盯着进度看,授权提示不能只在模态框里)。
4203
+ // 这块卡片是要显眼的:警告色描边 + 倒计时 + 同意/取消,而不是一行小字。
4204
+ // 关掉兜底或勾了"不再提醒"时给出对应说明(这两种情况前端会自动同意/取消)。
4205
+ const autoNote = !aiFallback ? t("aiFallbackOff") : (aiRemember ? t("aiRememberNote") : null);
4206
+ const remain = consentRemaining(job);
4207
+ const lastError = job.aiConsent?.lastError ?? null;
4208
+ const progressLine = progressText(job.progress);
4209
+ return el("div", { key: job.jobId, className: styles.consentCard },
4010
4210
  el("div", { className: styles.rowTop },
4011
4211
  el("span", { className: styles.spinner }),
4012
- el("strong", { className: styles.name }, t("installingLocal") + ":" + (job.packageName ?? job.repo))),
4013
- el("p", { className: styles.message }, autoNote));
4212
+ el("strong", { className: styles.name }, t("aiConsentCardTitle")),
4213
+ remain !== null ? el("span", { className: styles.consentCountdown }, t("aiConsentCountdown") + " " + remain) : null),
4214
+ el("p", { className: styles.message }, t("installingLocal") + ":" + (job.packageName ?? job.repo)),
4215
+ progressLine !== "" ? el("p", { className: styles.progress }, progressLine) : null,
4216
+ el("p", { className: styles.message }, t("aiConsentText")),
4217
+ lastError !== null ? el("p", { className: styles.note }, t("aiConsentLastError") + ":" + lastError) : null,
4218
+ remain !== null ? el("p", { className: styles.note }, t("aiConsentTimeoutNote").replace("{min}", consentTimeoutMinutes(job))) : null,
4219
+ autoNote !== null ? el("p", { className: styles.note }, autoNote) : null,
4220
+ el("div", { className: styles.rowTop },
4221
+ el("button", { type: "button", className: styles.consentApprove, onClick: () => aiConsent(job.jobId, true) }, t("aiConsentApprove")),
4222
+ el("button", { type: "button", className: styles.toggle, onClick: () => aiConsent(job.jobId, false) }, t("aiConsentDecline"))));
4014
4223
  }
4015
4224
  const stageKey = job.stage === "preparing" ? "stagePreparing" : job.stage === "configuring" ? "stageConfiguring" : job.stage === "repairing" ? "stageRepairing" : "stageInstalling";
4016
4225
  const elapsed = Math.max(0, Math.round((Date.now() - (job.startedAt ?? Date.now())) / 1000));
4226
+ const progressLine = progressText(job.progress);
4017
4227
  return el("div", { key: job.jobId, className: styles.detail },
4018
4228
  el("div", { className: styles.rowTop },
4019
4229
  el("span", { className: styles.spinner }),
4020
4230
  el("strong", { className: styles.name }, t("installingLocal") + ":" + (job.packageName ?? job.repo))),
4231
+ progressLine !== "" ? el("p", { className: styles.progress }, progressLine) : null,
4021
4232
  el("p", { className: styles.status },
4022
4233
  t("stageLabel") + ":" + t(stageKey) + " · " + t("elapsed") + " " + elapsed + "s"));
4023
4234
  }),
@@ -4148,7 +4359,7 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
4148
4359
  ? el("div", { className: styles.detail },
4149
4360
  el("p", { className: styles.status, "data-error": "true" }, t("error")),
4150
4361
  el("button", { type: "button", className: styles.toggle, onClick: refresh }, t("retry")))
4151
- : state.data.entries.length === 0
4362
+ : state.data.entries.length === 0 && pendingCards.length === 0
4152
4363
  ? el("p", { className: styles.status }, t("empty"))
4153
4364
  : installedQueryNorm !== "" && rows.length === 0
4154
4365
  ? el("p", { className: styles.status }, t("noMatch"))
@@ -4181,26 +4392,36 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
4181
4392
  (() => {
4182
4393
  // 最上层模态框:需要 AI 兜底授权时无论如何都让用户看到
4183
4394
  const consentJob = Object.values(jobs).find((job) => job.status === "installing" && job.stage === "ai-consent" && aiFallback && !aiRemember);
4184
- return consentJob === undefined
4185
- ? null
4186
- : el("div", { className: styles.modalBackdrop },
4187
- el("div", { className: styles.modalCard },
4188
- el("strong", { className: styles.name }, t("installingLocal") + ":" + (consentJob.packageName ?? consentJob.repo)),
4189
- el("p", { className: styles.message }, t("aiConsentText")),
4190
- el("label", { className: styles.consentRemember },
4191
- el("input", {
4192
- type: "checkbox",
4193
- checked: aiRemember,
4194
- onChange: (event) => {
4195
- const checked = event.currentTarget.checked;
4196
- setAiRemember(checked);
4197
- try { localStorage.setItem("pc-ai-remember", checked ? "1" : "0"); } catch {}
4198
- },
4199
- }),
4200
- t("aiConsentRemember")),
4201
- el("div", { className: styles.rowTop },
4202
- el("button", { type: "button", className: styles.toggle, onClick: () => { try { localStorage.setItem("pc-ai-remember", aiRemember ? "1" : "0"); } catch {} aiConsent(consentJob.jobId, true); } }, t("aiConsentApprove")),
4203
- el("button", { type: "button", className: styles.toggle, onClick: () => aiConsent(consentJob.jobId, false) }, t("aiConsentDecline")))));
4395
+ if (consentJob === undefined) return null;
4396
+ // 倒计时与进度也放进模态框:用户在模态框上做决定,不用回去翻进度区
4397
+ const remain = consentRemaining(consentJob);
4398
+ const lastError = consentJob.aiConsent?.lastError ?? null;
4399
+ const progressLine = progressText(consentJob.progress);
4400
+ return el("div", { className: styles.modalBackdrop },
4401
+ el("div", { className: styles.modalCard },
4402
+ el("strong", { className: styles.name }, t("aiConsentCardTitle")),
4403
+ el("p", { className: styles.message }, t("installingLocal") + ":" + (consentJob.packageName ?? consentJob.repo)),
4404
+ progressLine !== "" ? el("p", { className: styles.progress }, progressLine) : null,
4405
+ remain !== null
4406
+ ? el("p", { className: styles.message, "data-warn": "true" },
4407
+ t("aiConsentCountdown") + " " + remain + " · " + t("aiConsentTimeoutNote").replace("{min}", consentTimeoutMinutes(consentJob)))
4408
+ : null,
4409
+ el("p", { className: styles.message }, t("aiConsentText")),
4410
+ lastError !== null ? el("p", { className: styles.note }, t("aiConsentLastError") + ":" + lastError) : null,
4411
+ el("label", { className: styles.consentRemember },
4412
+ el("input", {
4413
+ type: "checkbox",
4414
+ checked: aiRemember,
4415
+ onChange: (event) => {
4416
+ const checked = event.currentTarget.checked;
4417
+ setAiRemember(checked);
4418
+ try { localStorage.setItem("pc-ai-remember", checked ? "1" : "0"); } catch {}
4419
+ },
4420
+ }),
4421
+ t("aiConsentRemember")),
4422
+ el("div", { className: styles.rowTop },
4423
+ el("button", { type: "button", className: styles.consentApprove, onClick: () => { try { localStorage.setItem("pc-ai-remember", aiRemember ? "1" : "0"); } catch {} aiConsent(consentJob.jobId, true); } }, t("aiConsentApprove")),
4424
+ el("button", { type: "button", className: styles.toggle, onClick: () => aiConsent(consentJob.jobId, false) }, t("aiConsentDecline")))));
4204
4425
  })(),
4205
4426
  sourcesOpen
4206
4427
  ? el("div", { className: styles.modalBackdrop },