dshmarket 1.3.0 → 1.4.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 fkysly and dsh-market contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -51,6 +51,10 @@ Installs prefer npm tarballs over full-repo GitHub downloads whenever a plugin p
51
51
  - For terminal-attached launches, the detached replacement keeps running after the original terminal closes
52
52
  - Listing ≠ endorsement: plugins are third-party code, install sources you trust
53
53
 
54
+ ## Submit your plugin
55
+
56
+ **This repo is the market app, not the catalog.** The plugin list comes from the curated [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) registry — to get your plugin listed in the market, open a PR **there** (one entry in the list; the site and this market pick it up automatically, usually within a day). Please don't PR plugin entries against this repo.
57
+
54
58
  ## Data source
55
59
 
56
60
  Live from [awesome-dsh-plugin.com/plugins.json](https://awesome-dsh-plugin.com/plugins.json) — curated entries, npm mapping, and star counts refreshed daily by CI — with a bundled snapshot as offline fallback.
package/README.zh.md CHANGED
@@ -51,6 +51,10 @@ dsh plugin --profile web add dshmarket
51
51
  - 从终端启动时,替代进程脱离原终端,关闭原终端后仍会继续运行
52
52
  - 收录 ≠ 背书:插件是第三方代码,请只安装你信任的来源
53
53
 
54
+ ## 提交你的插件
55
+
56
+ **这个仓库是市场应用本身,不是插件目录。** 市场里的插件列表来自精选列表 [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)——想让你的插件上架,请去**那边**提 PR(在列表里加一条即可,站点和本市场会自动收录,通常一天内生效)。请不要往本仓库提插件条目。
57
+
54
58
  ## 数据源
55
59
 
56
60
  实时来自 [awesome-dsh-plugin.com/plugins.json](https://awesome-dsh-plugin.com/plugins.json)——精选条目、npm 映射、star 数由 CI 每日刷新——内置快照做离线兜底。
package/client/client.js CHANGED
@@ -66,6 +66,12 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
66
66
  catsLess: "收起",
67
67
  sortHot: "最热",
68
68
  sortNew: "最新",
69
+ prevPage: "上一页",
70
+ nextPage: "下一页",
71
+ firstPage: "首页",
72
+ lastPage: "末页",
73
+ pageInfo: "第 {0} / {1} 页",
74
+ perPage: "每页",
69
75
  marketUpdate: "市场有新版本,升级",
70
76
  updateAll: "全部更新",
71
77
  tabThemes: "主题",
@@ -134,6 +140,12 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
134
140
  catsLess: "Less",
135
141
  sortHot: "Top",
136
142
  sortNew: "New",
143
+ prevPage: "Previous",
144
+ nextPage: "Next",
145
+ firstPage: "First",
146
+ lastPage: "Last",
147
+ pageInfo: "Page {0} of {1}",
148
+ perPage: "Per page",
137
149
  marketUpdate: "Market update available — upgrade",
138
150
  updateAll: "Update all",
139
151
  tabThemes: "Themes",
@@ -193,6 +205,29 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
193
205
  return open || active === "all" ? categories : [active, ...categories.filter((id) => id !== active)];
194
206
  }
195
207
  /**
208
+ * Page-number list for the discover pager. With few pages it is simply
209
+ * 1..total; with many it windows around the current page and inserts '…'
210
+ * so a 400-plugin catalog stays a compact `1 … 4 5 6 … 17` instead of a
211
+ * long row of numbered buttons. Always begins with 1 and ends with total.
212
+ */
213
+ function pageItems(current, total) {
214
+ if (total <= 7) {
215
+ const all = [];
216
+ for (let i = 1; i <= total; i++) all.push(i);
217
+ return all;
218
+ }
219
+ const items = [1];
220
+ let start = Math.max(2, current - 1);
221
+ let end = Math.min(total - 1, current + 1);
222
+ if (current <= 4) end = 5;
223
+ if (current >= total - 3) start = total - 4;
224
+ if (start > 2) items.push("…");
225
+ for (let i = start; i <= end; i++) items.push(i);
226
+ if (end < total - 1) items.push("…");
227
+ items.push(total);
228
+ return items;
229
+ }
230
+ /**
196
231
  * Unified installed-state matching (#15): both sides collapse to lowercase
197
232
  * identity sets — the registry entry contributes its bare name, npm name and
198
233
  * owner/repo; the dependency contributes its key and the repo inside its
@@ -235,10 +270,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
235
270
  return matchInstalledName(plugin, installed) !== null;
236
271
  }
237
272
  /**
238
- * The brand mark (assets/logo.svg shared block-grid mark with
239
- * awesome-dsh-plugin), inlined so the header needs no extra request.
273
+ * The header brand mark now lives in MarketSection.tsx as an inline SVG
274
+ * (official-style monochrome glyph, fill="currentColor") so it follows the
275
+ * active theme; the colored assets/logo.svg tile is no longer inlined here.
240
276
  */
241
- const LOGO_URI = "data:image/svg+xml," + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 128 128\"><rect width=\"128\" height=\"128\" rx=\"28\" fill=\"#f6f2ea\"/><g transform=\"translate(15.7 16.7) scale(0.3) translate(-112 -78)\"><g fill=\"#2b2620\"><rect x=\"112\" y=\"112\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"212\" y=\"112\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"112\" y=\"212\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"212\" y=\"212\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"112\" y=\"312\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"212\" y=\"312\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"312\" y=\"212\" width=\"88\" height=\"88\" rx=\"14\"/><rect x=\"312\" y=\"312\" width=\"88\" height=\"88\" rx=\"14\"/></g><rect x=\"346\" y=\"78\" width=\"88\" height=\"88\" rx=\"14\" fill=\"#c0392b\" transform=\"rotate(9 390 122)\"/></g></svg>");
242
277
  /** Four representative colors for a theme card's preview strip. */
243
278
  function themeSwatch(def) {
244
279
  const tk = def.tokens || {};
@@ -282,7 +317,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
282
317
  }
283
318
  //#endregion
284
319
  //#region \0dsh-css:/home/runner/work/dsh-market/dsh-market/src/client/Market.module.css.mjs
285
- const css = ".eGUBIq_root{min-width:0;height:100%;color:var(--dsw-alias-label-primary,#1f2328);flex-direction:column;display:flex;position:relative}.eGUBIq_head{flex-direction:column;gap:12px;padding:4px 4px 12px;display:flex}.eGUBIq_title{margin:0;font-size:16px;font-weight:500;line-height:24px}.eGUBIq_sub{color:var(--dsw-alias-label-tertiary,#8b93a1);margin:0;font-size:14px;line-height:22px}.eGUBIq_searchInline{flex-shrink:0;width:200px;margin-bottom:6px}.eGUBIq_tabs{border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);align-items:flex-end;gap:2px;display:flex}.eGUBIq_tab{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;padding:7px 12px;font-size:13px}.eGUBIq_tab.eGUBIq_on{color:var(--dsw-alias-brand-primary,#4f6ef7);border-bottom-color:var(--dsw-alias-brand-primary,#4f6ef7);font-weight:600}.eGUBIq_restart{background:var(--dsw-alias-bg-layer-2,#fdf3e3);border:1px solid var(--dsw-alias-border-l2,#f3e3c3);border-radius:8px;align-items:center;gap:8px;margin:0;padding:8px 12px;font-size:12px;display:flex}.eGUBIq_body{flex:1;padding:12px 4px 24px;overflow-x:hidden;overflow-y:auto}.eGUBIq_cats{z-index:5;background:var(--dsw-alias-bg-layer-2,#f7f8fa);margin:-12px -4px 2px;padding:12px 4px 4px;position:sticky;top:-13px}.eGUBIq_catsRow{align-items:flex-start;gap:8px;display:flex;position:relative}.eGUBIq_sort{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#e5e7eb);border-radius:8px;flex-shrink:0;gap:2px;padding:2px;display:flex}.eGUBIq_sort button{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;background:0 0;border:none;border-radius:6px;padding:3px 10px;font-size:12px}.eGUBIq_sort button.eGUBIq_on{background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font-weight:600;box-shadow:0 1px 3px #00000014}.eGUBIq_star{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px}.eGUBIq_top{z-index:20;border:1px solid var(--dsw-alias-border-l1,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);width:38px;height:38px;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;border-radius:99px;font-size:16px;position:absolute;bottom:18px;right:18px;box-shadow:0 4px 14px #0000001f}.eGUBIq_top:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_tag{border:1px solid var(--dsw-alias-border-l3,#d9dde3);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:4px;flex-shrink:0;padding:1px 6px;font-size:11px;line-height:16px}.eGUBIq_okState{color:var(--dsw-alias-state-success-primary,#16a34a);white-space:nowrap;font-size:12px;font-weight:600}.eGUBIq_dangerBtn.eGUBIq_dangerBtn{border-color:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-state-error-primary,#dc2626)}.eGUBIq_dangerArmed.eGUBIq_dangerArmed{background:var(--dsw-alias-state-error-primary,#dc2626);color:#fff}.eGUBIq_warnBtn.eGUBIq_warnBtn{background:var(--dsw-alias-state-warn-primary,#ea580c);color:#fff}.eGUBIq_catsWrap{flex-wrap:wrap;flex:1;align-items:center;gap:6px;min-width:0;display:flex}.eGUBIq_catsCollapsed{max-height:62px;overflow:hidden}.eGUBIq_catsToggle.eGUBIq_catsToggle{height:26px;min-height:26px;color:var(--dsw-alias-label-secondary,#6b7280);padding:0 6px}.eGUBIq_cmdDetails{margin:0}.eGUBIq_cmdSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary,#6b7280);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.eGUBIq_cmdSummary::-webkit-details-marker{display:none}.eGUBIq_cmdSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.eGUBIq_cmdDetails[open]>.eGUBIq_cmdSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.eGUBIq_cmdSummary:hover{color:var(--dsw-alias-label-primary,#1f2328)}.eGUBIq_cmd{background:var(--dsw-alias-bg-layer-2,#f3f4f6);word-break:break-all;border-radius:6px;margin:8px 0 0;padding:8px 10px;font-family:ui-monospace,Menlo,monospace;font-size:11px;line-height:18px}.eGUBIq_warnLine{color:var(--dsw-alias-state-warn-primary,#b45309);margin:0;font-size:12px;font-weight:600;line-height:18px}.eGUBIq_modalNote{color:var(--dsw-alias-label-tertiary,#8b93a1);margin:12px 0 0;font-size:12px;line-height:18px}.eGUBIq_grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;display:grid}.eGUBIq_sect{color:var(--dsw-alias-label-secondary,#6b7280);margin:14px 2px 8px;font-size:12px;font-weight:600}.eGUBIq_sect:first-child{margin-top:2px}.eGUBIq_swatches{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;gap:0;height:34px;display:flex;overflow:hidden}.eGUBIq_themesGrid{margin-bottom:12px}.eGUBIq_swatches i{flex:1}.eGUBIq_card{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.eGUBIq_row1{align-items:center;gap:10px;min-width:0;display:flex}.eGUBIq_av{color:#fff;object-fit:cover;background:var(--dsw-alias-bg-layer-2,#f3f4f6);border-radius:8px;flex-shrink:0;place-items:center;width:32px;height:32px;font-size:14px;font-weight:700;display:grid}.eGUBIq_nm{text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:500;line-height:22px;overflow:hidden}.eGUBIq_owner{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px}.eGUBIq_desc{color:var(--dsw-alias-label-tertiary,#8b93a1);min-height:36px;margin:0;font-size:12px;line-height:18px}.eGUBIq_foot{align-items:center;gap:8px;margin-top:auto;display:flex}.eGUBIq_grow{flex:1}.eGUBIq_titleRow{align-items:center;gap:10px;display:flex}.eGUBIq_descTight{min-height:0}.eGUBIq_src{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px;text-decoration:none}.eGUBIq_src:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_dot{vertical-align:2px;margin-left:5px}.eGUBIq_loading{color:var(--dsw-alias-label-secondary,#9ca3af);flex-direction:column;align-items:center;gap:12px;padding:48px;font-size:13px;display:flex}.eGUBIq_spin{border:3px solid var(--dsw-alias-border-l1,#e5e7eb);border-top-color:var(--dsw-alias-brand-primary,#4f6ef7);border-radius:99px;width:22px;height:22px;animation:.8s linear infinite eGUBIq_sp}@keyframes eGUBIq_sp{to{transform:rotate(360deg)}}.eGUBIq_progress{background:var(--dsw-alias-bg-layer-2,#f3f4f6);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:8px;align-items:center;gap:9px;margin:0;padding:8px 12px;font-size:12px;display:flex}.eGUBIq_irow .eGUBIq_progress{margin-top:8px}.eGUBIq_progress .eGUBIq_spin{border-width:2px;flex-shrink:0;width:14px;height:14px}.eGUBIq_progress code{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,Menlo,monospace;font-size:11px;overflow:hidden}.eGUBIq_empty{color:var(--dsw-alias-label-secondary,#9ca3af);text-align:center;padding:32px;font-size:13px}.eGUBIq_err{color:var(--dsw-alias-state-error-primary,#dc2626);white-space:pre-wrap;word-break:break-all;margin:8px 0;font-size:12px}.eGUBIq_irow{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;align-items:center;gap:10px;margin-bottom:8px;padding:12px 14px;display:flex}.eGUBIq_irow>.eGUBIq_src,.eGUBIq_irow>.eGUBIq_owner,.eGUBIq_dangerBtn.eGUBIq_dangerBtn,.eGUBIq_warnBtn.eGUBIq_warnBtn,.eGUBIq_dangerArmed.eGUBIq_dangerArmed{white-space:nowrap;flex-shrink:0}.eGUBIq_spec{color:var(--dsw-alias-label-secondary,#9ca3af);font-family:ui-monospace,Menlo,monospace;font-size:11px}.eGUBIq_staleAction{margin-top:8px}.eGUBIq_pct{color:var(--dsw-alias-label-secondary,#6b7280);flex-shrink:0;font-size:11px;font-weight:600}.eGUBIq_cancelBtn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#6b7280);font:inherit;cursor:pointer;white-space:nowrap;border-radius:6px;flex-shrink:0;padding:2px 10px;font-size:11px;line-height:16px}.eGUBIq_cancelBtn:hover{color:var(--dsw-alias-state-error-primary,#dc2626);border-color:var(--dsw-alias-state-error-primary,#dc2626)}.eGUBIq_sentinel{height:1px}";
320
+ const css = ".eGUBIq_root{min-width:0;height:100%;color:var(--dsw-alias-label-primary,#1f2328);flex-direction:column;display:flex;position:relative}.eGUBIq_head{flex-direction:column;gap:12px;padding:4px 4px 12px;display:flex}.eGUBIq_title{margin:0;font-size:16px;font-weight:500;line-height:24px}.eGUBIq_sub{color:var(--dsw-alias-label-tertiary,#8b93a1);margin:0;font-size:14px;line-height:22px}.eGUBIq_searchInline{flex-shrink:0;width:200px;margin-bottom:6px}.eGUBIq_tabs{border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);align-items:flex-end;gap:2px;display:flex}.eGUBIq_tab{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;padding:7px 12px;font-size:13px}.eGUBIq_tab.eGUBIq_on{color:var(--dsw-alias-brand-primary,#4f6ef7);border-bottom-color:var(--dsw-alias-brand-primary,#4f6ef7);font-weight:600}.eGUBIq_restart{background:var(--dsw-alias-bg-layer-2,#fdf3e3);border:1px solid var(--dsw-alias-border-l2,#f3e3c3);border-radius:8px;align-items:center;gap:8px;margin:0;padding:8px 12px;font-size:12px;display:flex}.eGUBIq_body{flex:1;padding:12px 4px 24px;overflow-x:hidden;overflow-y:auto}.eGUBIq_cats{z-index:5;background:var(--dsw-alias-bg-layer-2,#f7f8fa);margin:-12px -4px 2px;padding:12px 4px 4px;position:sticky;top:-13px}.eGUBIq_catsRow{align-items:flex-start;gap:8px;display:flex;position:relative}.eGUBIq_sort{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l1,#e5e7eb);border-radius:8px;flex-shrink:0;gap:2px;padding:2px;display:flex}.eGUBIq_sort button{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;background:0 0;border:none;border-radius:6px;padding:3px 10px;font-size:12px}.eGUBIq_sort button.eGUBIq_on{background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font-weight:600;box-shadow:0 1px 3px #00000014}.eGUBIq_star{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px}.eGUBIq_top{z-index:20;border:1px solid var(--dsw-alias-border-l1,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);width:38px;height:38px;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;border-radius:99px;font-size:16px;position:absolute;bottom:18px;right:18px;box-shadow:0 4px 14px #0000001f}.eGUBIq_top:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_tag{border:1px solid var(--dsw-alias-border-l3,#d9dde3);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:4px;flex-shrink:0;padding:1px 6px;font-size:11px;line-height:16px}.eGUBIq_okState{color:var(--dsw-alias-state-success-primary,#16a34a);white-space:nowrap;font-size:12px;font-weight:600}.eGUBIq_dangerBtn.eGUBIq_dangerBtn{border-color:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-state-error-primary,#dc2626)}.eGUBIq_dangerArmed.eGUBIq_dangerArmed{background:var(--dsw-alias-state-error-primary,#dc2626);color:#fff}.eGUBIq_warnBtn.eGUBIq_warnBtn{background:var(--dsw-alias-state-warn-primary,#ea580c);color:#fff}.eGUBIq_catsWrap{flex-wrap:wrap;flex:1;align-items:center;gap:6px;min-width:0;display:flex}.eGUBIq_catsCollapsed{max-height:62px;overflow:hidden}.eGUBIq_catsToggle.eGUBIq_catsToggle{height:26px;min-height:26px;color:var(--dsw-alias-label-secondary,#6b7280);padding:0 6px}.eGUBIq_cmdDetails{margin:0}.eGUBIq_cmdSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary,#6b7280);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.eGUBIq_cmdSummary::-webkit-details-marker{display:none}.eGUBIq_cmdSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.eGUBIq_cmdDetails[open]>.eGUBIq_cmdSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.eGUBIq_cmdSummary:hover{color:var(--dsw-alias-label-primary,#1f2328)}.eGUBIq_cmd{background:var(--dsw-alias-bg-layer-2,#f3f4f6);word-break:break-all;border-radius:6px;margin:8px 0 0;padding:8px 10px;font-family:ui-monospace,Menlo,monospace;font-size:11px;line-height:18px}.eGUBIq_warnLine{color:var(--dsw-alias-state-warn-primary,#b45309);margin:0;font-size:12px;font-weight:600;line-height:18px}.eGUBIq_modalNote{color:var(--dsw-alias-label-tertiary,#8b93a1);margin:12px 0 0;font-size:12px;line-height:18px}.eGUBIq_grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;display:grid}.eGUBIq_sect{color:var(--dsw-alias-label-secondary,#6b7280);margin:14px 2px 8px;font-size:12px;font-weight:600}.eGUBIq_sect:first-child{margin-top:2px}.eGUBIq_swatches{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;gap:0;height:34px;display:flex;overflow:hidden}.eGUBIq_themesGrid{margin-bottom:12px}.eGUBIq_swatches i{flex:1}.eGUBIq_card{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.eGUBIq_row1{align-items:center;gap:10px;min-width:0;display:flex}.eGUBIq_av{color:#fff;object-fit:cover;background:var(--dsw-alias-bg-layer-2,#f3f4f6);border-radius:8px;flex-shrink:0;place-items:center;width:32px;height:32px;font-size:14px;font-weight:700;display:grid}.eGUBIq_nm{text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:500;line-height:22px;overflow:hidden}.eGUBIq_owner{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px}.eGUBIq_desc{color:var(--dsw-alias-label-tertiary,#8b93a1);min-height:36px;margin:0;font-size:12px;line-height:18px}.eGUBIq_foot{align-items:center;gap:8px;margin-top:auto;display:flex}.eGUBIq_grow{flex:1}.eGUBIq_titleRow{align-items:center;gap:10px;display:flex}.eGUBIq_descTight{min-height:0}.eGUBIq_src{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px;text-decoration:none}.eGUBIq_src:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_dot{vertical-align:2px;margin-left:5px}.eGUBIq_loading{color:var(--dsw-alias-label-secondary,#9ca3af);flex-direction:column;align-items:center;gap:12px;padding:48px;font-size:13px;display:flex}.eGUBIq_spin{border:3px solid var(--dsw-alias-border-l1,#e5e7eb);border-top-color:var(--dsw-alias-brand-primary,#4f6ef7);border-radius:99px;width:22px;height:22px;animation:.8s linear infinite eGUBIq_sp}@keyframes eGUBIq_sp{to{transform:rotate(360deg)}}.eGUBIq_progress{background:var(--dsw-alias-bg-layer-2,#f3f4f6);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:8px;flex-wrap:wrap;align-items:center;gap:9px;margin:0;padding:8px 12px;font-size:12px;display:flex}.eGUBIq_bar{background:var(--dsw-alias-border-l1,#e5e7eb);border-radius:99px;width:100%;height:4px;overflow:hidden}.eGUBIq_barFill{background:var(--dsw-alias-brand-primary,#4f6ef7);border-radius:99px;height:100%;transition:width .6s}.eGUBIq_barWave{width:30%;animation:1.2s ease-in-out infinite eGUBIq_dshmSlide}@keyframes eGUBIq_dshmSlide{0%{margin-left:-30%}to{margin-left:100%}}.eGUBIq_irow .eGUBIq_progress{margin-top:8px}.eGUBIq_progress .eGUBIq_spin{border-width:2px;flex-shrink:0;width:14px;height:14px}.eGUBIq_progress code{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,Menlo,monospace;font-size:11px;overflow:hidden}.eGUBIq_empty{color:var(--dsw-alias-label-secondary,#9ca3af);text-align:center;padding:32px;font-size:13px}.eGUBIq_err{color:var(--dsw-alias-state-error-primary,#dc2626);white-space:pre-wrap;word-break:break-all;margin:8px 0;font-size:12px}.eGUBIq_irow{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;align-items:center;gap:10px;margin-bottom:8px;padding:12px 14px;display:flex}.eGUBIq_irow>.eGUBIq_src,.eGUBIq_irow>.eGUBIq_owner,.eGUBIq_dangerBtn.eGUBIq_dangerBtn,.eGUBIq_warnBtn.eGUBIq_warnBtn,.eGUBIq_dangerArmed.eGUBIq_dangerArmed{white-space:nowrap;flex-shrink:0}.eGUBIq_spec{color:var(--dsw-alias-label-secondary,#9ca3af);font-family:ui-monospace,Menlo,monospace;font-size:11px}.eGUBIq_staleAction{margin-top:8px}.eGUBIq_pct{color:var(--dsw-alias-label-secondary,#6b7280);flex-shrink:0;font-size:11px;font-weight:600}.eGUBIq_cancelBtn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#6b7280);font:inherit;cursor:pointer;white-space:nowrap;border-radius:6px;flex-shrink:0;padding:2px 10px;font-size:11px;line-height:16px}.eGUBIq_cancelBtn:hover{color:var(--dsw-alias-state-error-primary,#dc2626);border-color:var(--dsw-alias-state-error-primary,#dc2626)}.eGUBIq_pager{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;margin:16px 0 4px;display:flex}.eGUBIq_pagerPages{flex-wrap:wrap;flex:1;justify-content:center;align-items:center;gap:6px;min-width:0;display:flex}.eGUBIq_pagerSize{flex-shrink:0;align-items:center;gap:4px;display:flex}.eGUBIq_sizeLabel{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px}.eGUBIq_sizeBtn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#6b7280);font:inherit;cursor:pointer;border-radius:6px;padding:3px 8px;font-size:12px;line-height:18px}.eGUBIq_sizeBtn:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_sizeOn{background:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7);color:#fff;font-weight:600}.eGUBIq_pageBtn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#6b7280);font:inherit;cursor:pointer;border-radius:6px;min-width:28px;padding:4px 10px;font-size:12px;line-height:18px}.eGUBIq_pageBtn:hover:not(:disabled){color:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_pageBtn:disabled{opacity:.45;cursor:default}.eGUBIq_pageOn{background:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7);color:#fff;font-weight:600}.eGUBIq_pageEllipsis{color:var(--dsw-alias-label-secondary,#9ca3af);padding:0 2px;font-size:12px}.eGUBIq_pageInfo{color:var(--dsw-alias-label-secondary,#6b7280);white-space:nowrap;font-size:12px}";
286
321
  const tagId = "dshmarket/Market.module.css";
287
322
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
288
323
  const tag = document.createElement("style");
@@ -292,62 +327,75 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
292
327
  document.head.appendChild(tag);
293
328
  }
294
329
  var Market_module_css_default = {
295
- "dangerBtn": "eGUBIq_dangerBtn",
296
- "top": "eGUBIq_top",
297
330
  "catsWrap": "eGUBIq_catsWrap",
298
- "catsToggle": "eGUBIq_catsToggle",
299
- "grid": "eGUBIq_grid",
300
- "grow": "eGUBIq_grow",
301
- "titleRow": "eGUBIq_titleRow",
302
- "staleAction": "eGUBIq_staleAction",
303
- "modalNote": "eGUBIq_modalNote",
304
- "star": "eGUBIq_star",
305
- "sect": "eGUBIq_sect",
306
- "row1": "eGUBIq_row1",
307
- "av": "eGUBIq_av",
308
- "warnBtn": "eGUBIq_warnBtn",
309
- "pct": "eGUBIq_pct",
310
- "loading": "eGUBIq_loading",
311
- "cancelBtn": "eGUBIq_cancelBtn",
312
- "spec": "eGUBIq_spec",
313
- "catsRow": "eGUBIq_catsRow",
331
+ "pageInfo": "eGUBIq_pageInfo",
314
332
  "cats": "eGUBIq_cats",
333
+ "catsRow": "eGUBIq_catsRow",
315
334
  "themesGrid": "eGUBIq_themesGrid",
335
+ "star": "eGUBIq_star",
336
+ "grow": "eGUBIq_grow",
337
+ "card": "eGUBIq_card",
338
+ "pagerPages": "eGUBIq_pagerPages",
339
+ "pageEllipsis": "eGUBIq_pageEllipsis",
340
+ "pager": "eGUBIq_pager",
341
+ "sort": "eGUBIq_sort",
342
+ "warnBtn": "eGUBIq_warnBtn",
316
343
  "progress": "eGUBIq_progress",
344
+ "okState": "eGUBIq_okState",
345
+ "root": "eGUBIq_root",
346
+ "sub": "eGUBIq_sub",
347
+ "pageOn": "eGUBIq_pageOn",
348
+ "tabs": "eGUBIq_tabs",
349
+ "swatches": "eGUBIq_swatches",
317
350
  "descTight": "eGUBIq_descTight",
318
351
  "irow": "eGUBIq_irow",
319
- "card": "eGUBIq_card",
352
+ "sect": "eGUBIq_sect",
353
+ "foot": "eGUBIq_foot",
354
+ "sizeBtn": "eGUBIq_sizeBtn",
355
+ "staleAction": "eGUBIq_staleAction",
356
+ "pct": "eGUBIq_pct",
357
+ "barWave": "eGUBIq_barWave",
358
+ "catsToggle": "eGUBIq_catsToggle",
320
359
  "owner": "eGUBIq_owner",
321
- "warnLine": "eGUBIq_warnLine",
322
- "nm": "eGUBIq_nm",
323
- "dangerArmed": "eGUBIq_dangerArmed",
324
- "desc": "eGUBIq_desc",
325
- "empty": "eGUBIq_empty",
326
- "src": "eGUBIq_src",
327
360
  "body": "eGUBIq_body",
328
- "err": "eGUBIq_err",
329
- "catsCollapsed": "eGUBIq_catsCollapsed",
330
- "tab": "eGUBIq_tab",
331
- "on": "eGUBIq_on",
332
- "sort": "eGUBIq_sort",
361
+ "av": "eGUBIq_av",
333
362
  "cmd": "eGUBIq_cmd",
334
- "tag": "eGUBIq_tag",
363
+ "err": "eGUBIq_err",
335
364
  "head": "eGUBIq_head",
336
- "okState": "eGUBIq_okState",
337
- "root": "eGUBIq_root",
338
- "tabs": "eGUBIq_tabs",
365
+ "spec": "eGUBIq_spec",
366
+ "grid": "eGUBIq_grid",
367
+ "cancelBtn": "eGUBIq_cancelBtn",
368
+ "pagerSize": "eGUBIq_pagerSize",
369
+ "sizeLabel": "eGUBIq_sizeLabel",
370
+ "cmdSummary": "eGUBIq_cmdSummary",
371
+ "loading": "eGUBIq_loading",
372
+ "top": "eGUBIq_top",
373
+ "cmdDetails": "eGUBIq_cmdDetails",
374
+ "src": "eGUBIq_src",
375
+ "on": "eGUBIq_on",
376
+ "catsCollapsed": "eGUBIq_catsCollapsed",
377
+ "restart": "eGUBIq_restart",
378
+ "dangerArmed": "eGUBIq_dangerArmed",
379
+ "dangerBtn": "eGUBIq_dangerBtn",
339
380
  "dot": "eGUBIq_dot",
340
- "title": "eGUBIq_title",
341
381
  "sp": "eGUBIq_sp",
342
- "sub": "eGUBIq_sub",
343
- "restart": "eGUBIq_restart",
344
- "swatches": "eGUBIq_swatches",
345
- "cmdDetails": "eGUBIq_cmdDetails",
346
- "foot": "eGUBIq_foot",
382
+ "desc": "eGUBIq_desc",
383
+ "bar": "eGUBIq_bar",
384
+ "empty": "eGUBIq_empty",
385
+ "warnLine": "eGUBIq_warnLine",
386
+ "pageBtn": "eGUBIq_pageBtn",
387
+ "dshmSlide": "eGUBIq_dshmSlide",
388
+ "tag": "eGUBIq_tag",
389
+ "nm": "eGUBIq_nm",
347
390
  "spin": "eGUBIq_spin",
348
- "sentinel": "eGUBIq_sentinel",
349
- "cmdSummary": "eGUBIq_cmdSummary",
350
- "searchInline": "eGUBIq_searchInline"
391
+ "searchInline": "eGUBIq_searchInline",
392
+ "sizeOn": "eGUBIq_sizeOn",
393
+ "barFill": "eGUBIq_barFill",
394
+ "title": "eGUBIq_title",
395
+ "titleRow": "eGUBIq_titleRow",
396
+ "tab": "eGUBIq_tab",
397
+ "row1": "eGUBIq_row1",
398
+ "modalNote": "eGUBIq_modalNote"
351
399
  };
352
400
  //#endregion
353
401
  //#region src/client/MarketSection.tsx
@@ -376,12 +424,44 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
376
424
  });
377
425
  }
378
426
  /**
427
+ * Official-style market glyph: the shared block-grid brand mark converted to
428
+ * the official monochrome icon form (16×16, fill="currentColor") so it
429
+ * follows the active theme. Mirrors the settings-nav glyph used for the
430
+ * "market" section id.
431
+ */
432
+ function MarketLogo({ size = 16, style }) {
433
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
434
+ width: size,
435
+ height: size,
436
+ viewBox: "0 0 16 16",
437
+ fill: "none",
438
+ xmlns: "http://www.w3.org/2000/svg",
439
+ "aria-hidden": "true",
440
+ style,
441
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
442
+ fill: "currentColor",
443
+ d: "M2.35 1.75H4.95A0.6 0.6 0 0 1 5.55 2.35V4.95A0.6 0.6 0 0 1 4.95 5.55H2.35A0.6 0.6 0 0 1 1.75 4.95V2.35A0.6 0.6 0 0 1 2.35 1.75ZM6.7 1.75H9.3A0.6 0.6 0 0 1 9.9 2.35V4.95A0.6 0.6 0 0 1 9.3 5.55H6.7A0.6 0.6 0 0 1 6.1 4.95V2.35A0.6 0.6 0 0 1 6.7 1.75ZM2.35 6.1H4.95A0.6 0.6 0 0 1 5.55 6.7V9.3A0.6 0.6 0 0 1 4.95 9.9H2.35A0.6 0.6 0 0 1 1.75 9.3V6.7A0.6 0.6 0 0 1 2.35 6.1ZM6.7 6.1H9.3A0.6 0.6 0 0 1 9.9 6.7V9.3A0.6 0.6 0 0 1 9.3 9.9H6.7A0.6 0.6 0 0 1 6.1 9.3V6.7A0.6 0.6 0 0 1 6.7 6.1ZM11.05 6.1H13.65A0.6 0.6 0 0 1 14.25 6.7V9.3A0.6 0.6 0 0 1 13.65 9.9H11.05A0.6 0.6 0 0 1 10.45 9.3V6.7A0.6 0.6 0 0 1 11.05 6.1ZM2.35 10.45H4.95A0.6 0.6 0 0 1 5.55 11.05V13.65A0.6 0.6 0 0 1 4.95 14.25H2.35A0.6 0.6 0 0 1 1.75 13.65V11.05A0.6 0.6 0 0 1 2.35 10.45ZM6.7 10.45H9.3A0.6 0.6 0 0 1 9.9 11.05V13.65A0.6 0.6 0 0 1 9.3 14.25H6.7A0.6 0.6 0 0 1 6.1 13.65V11.05A0.6 0.6 0 0 1 6.7 10.45ZM11.05 10.45H13.65A0.6 0.6 0 0 1 14.25 11.05V13.65A0.6 0.6 0 0 1 13.65 14.25H11.05A0.6 0.6 0 0 1 10.45 13.65V11.05A0.6 0.6 0 0 1 11.05 10.45Z"
444
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
445
+ fill: "currentColor",
446
+ d: "M11.05 1.75H13.65A0.6 0.6 0 0 1 14.25 2.35V4.95A0.6 0.6 0 0 1 13.65 5.55H11.05A0.6 0.6 0 0 1 10.45 4.95V2.35A0.6 0.6 0 0 1 11.05 1.75Z",
447
+ transform: "rotate(9 12.35 3.65)"
448
+ })]
449
+ });
450
+ }
451
+ /**
379
452
  * Module-scope caches so re-entering the section renders instantly instead
380
453
  * of refetching and rebuilding from a spinner (#30 by @StarsTom). Module
381
454
  * state survives section switches; a background refetch keeps it current.
382
455
  */
383
456
  let cachedRegistry = null;
384
457
  let cachedInstalled = null;
458
+ /** Discover grid page-size choices — the catalog grows daily, so cap each page. */
459
+ const PAGE_SIZES = [
460
+ 24,
461
+ 48,
462
+ 96
463
+ ];
464
+ const DEFAULT_PAGE_SIZE = 24;
385
465
  function MarketSection(props) {
386
466
  const t = props.t;
387
467
  const localeSnap = (0, react.useSyncExternalStore)((cb) => props.locale.subscribe(cb), () => props.locale.getSnapshot());
@@ -404,23 +484,17 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
404
484
  const [cat, setCat] = (0, react.useState)("all");
405
485
  const [confirming, setConfirming] = (0, react.useState)(null);
406
486
  const [busyUrl, setBusyUrl] = (0, react.useState)(null);
487
+ /** Consecutive idle polls with a pending install that never landed (#32). */
488
+ const idleStrikes = (0, react.useRef)(0);
407
489
  const [doneUrls, setDoneUrls] = (0, react.useState)([]);
408
490
  const [installError, setInstallError] = (0, react.useState)(null);
409
491
  const [updates, setUpdates] = (0, react.useState)({});
410
492
  const [updatingName, setUpdatingName] = (0, react.useState)(null);
411
493
  const [staleName, setStaleName] = (0, react.useState)(null);
412
- /**
413
- * Cards rendered so far — the discover grid renders incrementally (60,
414
- * then +120 as the sentinel scrolls into view) instead of all 400+ at
415
- * once, which built a ~70k px DOM and made section switches lag (#30).
416
- */
417
- const [visibleCount, setVisibleCount] = (0, react.useState)(60);
418
- const sentinelRef = (0, react.useCallback)((node) => {
419
- if (node === null) return;
420
- new IntersectionObserver((entries) => {
421
- if (entries.some((entry) => entry.isIntersecting)) setVisibleCount((count) => count + 120);
422
- }, { rootMargin: "600px" }).observe(node);
423
- }, []);
494
+ /** 1-based discover page; reset to 1 whenever the list shape changes. */
495
+ const [page, setPage] = (0, react.useState)(1);
496
+ /** Cards per discover page; changing it jumps back to page 1. */
497
+ const [pageSize, setPageSize] = (0, react.useState)(DEFAULT_PAGE_SIZE);
424
498
  /** Determinate percent parsed from pnpm's Progress line, when available. */
425
499
  const [progressPct, setProgressPct] = (0, react.useState)(null);
426
500
  /** Blocked build scripts from the last install: enables approve-and-retry (#6). */
@@ -504,7 +578,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
504
578
  body: "{}"
505
579
  }).then((res) => res.json()).then((body) => {
506
580
  if (body.ok) setEnvReady(true);
507
- else setEnvFailed(true);
581
+ else {
582
+ setEnvFailed(true);
583
+ if (typeof body.error === "string") setInstallError(body.error);
584
+ }
508
585
  }).catch(() => setEnvFailed(true)).finally(() => setEnvFixing(false));
509
586
  }, []);
510
587
  (0, react.useEffect)(() => {
@@ -531,9 +608,15 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
531
608
  setInstalled(status.installed || {});
532
609
  if (readSession("dshm-pending") !== null && busyUrl !== null) {
533
610
  if (data !== null && data.plugins.some((p) => p.url === busyUrl && isInstalled(p, status.installed || {}))) {
611
+ idleStrikes.current = 0;
534
612
  sessionStorage.removeItem("dshm-pending");
535
613
  setDoneUrls((urls) => urls.includes(busyUrl) ? urls : urls.concat(busyUrl));
536
614
  setBusyUrl(null);
615
+ } else if (++idleStrikes.current >= 2) {
616
+ idleStrikes.current = 0;
617
+ sessionStorage.removeItem("dshm-pending");
618
+ setBusyUrl(null);
619
+ setInstallError(t("installFail") + " — " + t("exportLog"));
537
620
  }
538
621
  }
539
622
  }
@@ -558,12 +641,34 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
558
641
  sort
559
642
  ]);
560
643
  (0, react.useEffect)(() => {
561
- setVisibleCount(60);
644
+ setPage(1);
562
645
  }, [
563
646
  q,
564
647
  cat,
565
648
  sort
566
649
  ]);
650
+ const totalPages = Math.max(1, Math.ceil(plugins.length / pageSize));
651
+ const currentPage = Math.min(page, totalPages);
652
+ const pagePlugins = plugins.slice((currentPage - 1) * pageSize, currentPage * pageSize);
653
+ const scrollToTop = () => {
654
+ const el = bodyRef.current;
655
+ if (el) {
656
+ if (typeof el.scrollTo === "function") el.scrollTo({
657
+ top: 0,
658
+ behavior: "smooth"
659
+ });
660
+ else el.scrollTop = 0;
661
+ }
662
+ };
663
+ const goToPage = (next) => {
664
+ setPage(Math.max(1, Math.min(next, totalPages)));
665
+ scrollToTop();
666
+ };
667
+ const changePageSize = (size) => {
668
+ setPageSize(size);
669
+ setPage(1);
670
+ scrollToTop();
671
+ };
567
672
  const doInstall = (0, react.useCallback)((plugin) => {
568
673
  setBuildsSkipped(null);
569
674
  setConfirming(null);
@@ -855,6 +960,13 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
855
960
  className: Market_module_css_default.cancelBtn,
856
961
  onClick: doCancel,
857
962
  children: t("cancelOp")
963
+ }),
964
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
965
+ className: Market_module_css_default.bar,
966
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
967
+ className: progressPct !== null ? Market_module_css_default.barFill : `${Market_module_css_default.barFill} ${Market_module_css_default.barWave}`,
968
+ style: progressPct !== null ? { width: `${progressPct}%` } : void 0
969
+ })
858
970
  })
859
971
  ]
860
972
  })
@@ -1014,15 +1126,9 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1014
1126
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1015
1127
  className: Market_module_css_default.titleRow,
1016
1128
  children: [
1017
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
1018
- src: LOGO_URI,
1019
- width: 22,
1020
- height: 22,
1021
- alt: "",
1022
- style: {
1023
- borderRadius: "5px",
1024
- flexShrink: 0
1025
- }
1129
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarketLogo, {
1130
+ size: 22,
1131
+ style: { flexShrink: 0 }
1026
1132
  }),
1027
1133
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1028
1134
  className: Market_module_css_default.title,
@@ -1262,10 +1368,68 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1262
1368
  children: t("empty")
1263
1369
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1264
1370
  className: Market_module_css_default.grid,
1265
- children: plugins.slice(0, visibleCount).map(pluginCard)
1266
- }), plugins.length > visibleCount && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1267
- ref: sentinelRef,
1268
- className: Market_module_css_default.sentinel
1371
+ children: pagePlugins.map(pluginCard)
1372
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1373
+ className: Market_module_css_default.pager,
1374
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1375
+ className: Market_module_css_default.pagerPages,
1376
+ children: totalPages > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1377
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1378
+ type: "button",
1379
+ className: Market_module_css_default.pageBtn,
1380
+ disabled: currentPage === 1,
1381
+ onClick: () => goToPage(1),
1382
+ "aria-label": t("firstPage"),
1383
+ children: "«"
1384
+ }),
1385
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1386
+ type: "button",
1387
+ className: Market_module_css_default.pageBtn,
1388
+ disabled: currentPage === 1,
1389
+ onClick: () => goToPage(currentPage - 1),
1390
+ children: t("prevPage")
1391
+ }),
1392
+ pageItems(currentPage, totalPages).map((item, i) => item === "…" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1393
+ className: Market_module_css_default.pageEllipsis,
1394
+ children: "…"
1395
+ }, "e" + i) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1396
+ type: "button",
1397
+ className: item === currentPage ? `${Market_module_css_default.pageBtn} ${Market_module_css_default.pageOn}` : Market_module_css_default.pageBtn,
1398
+ onClick: () => goToPage(item),
1399
+ children: item
1400
+ }, item)),
1401
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1402
+ type: "button",
1403
+ className: Market_module_css_default.pageBtn,
1404
+ disabled: currentPage === totalPages,
1405
+ onClick: () => goToPage(currentPage + 1),
1406
+ children: t("nextPage")
1407
+ }),
1408
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1409
+ type: "button",
1410
+ className: Market_module_css_default.pageBtn,
1411
+ disabled: currentPage === totalPages,
1412
+ onClick: () => goToPage(totalPages),
1413
+ "aria-label": t("lastPage"),
1414
+ children: "»"
1415
+ }),
1416
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1417
+ className: Market_module_css_default.pageInfo,
1418
+ children: t("pageInfo").replace("{0}", String(currentPage)).replace("{1}", String(totalPages))
1419
+ })
1420
+ ] })
1421
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1422
+ className: Market_module_css_default.pagerSize,
1423
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1424
+ className: Market_module_css_default.sizeLabel,
1425
+ children: t("perPage")
1426
+ }), PAGE_SIZES.map((size) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1427
+ type: "button",
1428
+ className: size === pageSize ? `${Market_module_css_default.sizeBtn} ${Market_module_css_default.sizeOn}` : Market_module_css_default.sizeBtn,
1429
+ onClick: () => changePageSize(size),
1430
+ children: size
1431
+ }, size))]
1432
+ })]
1269
1433
  })] })] }) : tab === "themes" && themeSnap !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(() => {
1270
1434
  const extra = themeSnap.themes.filter((def) => def.id !== "light" && def.id !== "dark");
1271
1435
  return extra.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -1336,6 +1500,13 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1336
1500
  className: Market_module_css_default.cancelBtn,
1337
1501
  onClick: doCancel,
1338
1502
  children: t("cancelOp")
1503
+ }),
1504
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1505
+ className: Market_module_css_default.bar,
1506
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1507
+ className: progressPct !== null ? Market_module_css_default.barFill : `${Market_module_css_default.barFill} ${Market_module_css_default.barWave}`,
1508
+ style: progressPct !== null ? { width: `${progressPct}%` } : void 0
1509
+ })
1339
1510
  })
1340
1511
  ]
1341
1512
  })