dshmarket 1.36.0 → 1.37.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/client/client.js CHANGED
@@ -4320,6 +4320,71 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
4320
4320
  });
4321
4321
  }
4322
4322
  //#endregion
4323
+ //#region src/client/self-check.ts
4324
+ /**
4325
+ * What the browser can see about the market that the server cannot.
4326
+ *
4327
+ * This exists because of a pattern, not a hypothesis. #293 ("市场显示空白")
4328
+ * and #384 ("lightbox cannot be closed") are both reproducible for their
4329
+ * reporters and for nobody else, on clean profiles, across host versions.
4330
+ * Both investigations then stalled in the same place: I asked the reporter to
4331
+ * open a console and paste the result of an expression. That costs a day per
4332
+ * question, asks a non-developer to run code from a stranger, and is the one
4333
+ * step where a report goes quiet.
4334
+ *
4335
+ * Meanwhile the exported log — which the issue template already asks for, and
4336
+ * which reporters do send — describes only the server: dependencies, bundle
4337
+ * resolution, events. Everything it says was already true of a machine where
4338
+ * the bug does not happen. The interesting half was in a page nobody looked
4339
+ * at.
4340
+ *
4341
+ * So the browser answers the questions I have actually had to ask, in the
4342
+ * artefact people already know how to produce. Nothing here diagnoses
4343
+ * anything on its own; it is evidence, collected before it is needed.
4344
+ */
4345
+ /**
4346
+ * How many times this module has been evaluated in this page.
4347
+ *
4348
+ * Module scope runs once per module instance, so >1 means the market's client
4349
+ * bundle was loaded twice — two React copies, two of every module singleton,
4350
+ * two portal containers. That is a specific hypothesis for #384: two React
4351
+ * roots each attach delegated listeners to their own portal container, and a
4352
+ * click lands on whichever container is last in `body` while the handlers
4353
+ * live on the other one, so the buttons look present and do nothing.
4354
+ *
4355
+ * Counted on `window` rather than in a module variable for the obvious
4356
+ * reason: a module variable would be duplicated along with everything else
4357
+ * and each copy would confidently report 1.
4358
+ */
4359
+ const globals = globalThis;
4360
+ globals.__dshmarketClientLoads = (globals.__dshmarketClientLoads ?? 0) + 1;
4361
+ /** Whether `value` looks like a browser environment worth inspecting. */
4362
+ const hasDom = () => typeof document !== "undefined" && document.body !== null;
4363
+ /**
4364
+ * The browser-side section of the exported log.
4365
+ *
4366
+ * Deliberately facts, not verdicts. "portal containers: 2" is something a
4367
+ * reporter can paste without judging it, and something I can act on; "your
4368
+ * bundle is double-loaded" would be a guess printed in the user's face, and
4369
+ * wrong the first time a host legitimately renders two markets.
4370
+ * @returns the lines to append, or an empty array outside a browser.
4371
+ */
4372
+ function clientDiagnostics() {
4373
+ if (!hasDom()) return [];
4374
+ const portals = document.querySelectorAll("[data-dsh-market-portal]");
4375
+ const roots = document.querySelectorAll("[data-dsh-market-root]");
4376
+ const last = portals.length > 0 ? portals[portals.length - 1] : null;
4377
+ return [
4378
+ `client bundle evaluations: ${String(globals.__dshmarketClientLoads ?? 0)}`,
4379
+ `market roots in the document: ${String(roots.length)}`,
4380
+ `portal containers: ${String(portals.length)}` + (last === null ? "" : ` (last one is body's last child: ${String(last === document.body.lastElementChild)})`),
4381
+ `plugin cards rendered: ${String(document.querySelectorAll("[data-dsh-market-root] [class*=\"_card\"]").length)}`,
4382
+ `document baseURI: ${document.baseURI}`,
4383
+ `page URL: ${location.origin}${location.pathname}`,
4384
+ `user agent: ${navigator.userAgent}`
4385
+ ];
4386
+ }
4387
+ //#endregion
4323
4388
  //#region src/client/MarketSection.tsx
4324
4389
  /**
4325
4390
  * The Market settings section: Discover / Themes / Installed tabs over the
@@ -5475,7 +5540,13 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
5475
5540
  setExportState("busy");
5476
5541
  fetch(api("/dsh-market/logs")).then(async (res) => {
5477
5542
  if (!res.ok) throw new Error(`HTTP ${String(res.status)}`);
5478
- const blob = await res.blob();
5543
+ const serverText = await res.text();
5544
+ const browser = clientDiagnostics();
5545
+ const blob = new Blob([serverText, ...browser.length > 0 ? [
5546
+ "## browser\n",
5547
+ browser.join("\n"),
5548
+ "\n"
5549
+ ] : []], { type: "text/plain;charset=utf-8" });
5479
5550
  const url = URL.createObjectURL(blob);
5480
5551
  const anchor = document.createElement("a");
5481
5552
  anchor.href = url;
@@ -6392,7 +6463,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
6392
6463
  });
6393
6464
  setUpdateNotes(null);
6394
6465
  setNotesState("loading");
6395
- fetch(`/dsh-market/changelog?name=${encodeURIComponent(name)}`).then((res) => res.json()).then((body) => {
6466
+ fetch(`${api("/dsh-market/changelog")}?name=${encodeURIComponent(name)}`).then((res) => res.json()).then((body) => {
6396
6467
  setUpdateNotes(body);
6397
6468
  setNotesState("ready");
6398
6469
  }).catch(() => setNotesState("fail"));
@@ -6427,7 +6498,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
6427
6498
  /** Write (or clear, when empty) this plugin's note. */
6428
6499
  const saveNote = (0, react.useCallback)((name, text) => {
6429
6500
  setNotingName(null);
6430
- fetch("/dsh-market/note", {
6501
+ fetch(api("/dsh-market/note"), {
6431
6502
  method: "POST",
6432
6503
  headers: { "content-type": "application/json" },
6433
6504
  body: JSON.stringify({
@@ -6458,7 +6529,9 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
6458
6529
  }))).then(({ status, body }) => {
6459
6530
  if (status === 200 && body.ok) {
6460
6531
  if (!body.hot) setRemovedCount((n) => n + 1);
6461
- clearPendingRefresh(name);
6532
+ const neverLoadedHere = hotNames.includes(name) || refreshNames.includes(name);
6533
+ if (body.refresh === true && !neverLoadedHere) setRefreshNames((names) => names.includes(name) ? names : names.concat(name));
6534
+ else clearPendingRefresh(name);
6462
6535
  refreshInstalled();
6463
6536
  } else {
6464
6537
  if (body.cancelled === true) {
@@ -6477,7 +6550,11 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
6477
6550
  setInstallError((text(body.error) || humanOutput(text(body.stderr)) || "error").trim().slice(-600));
6478
6551
  }
6479
6552
  }).catch((error) => setInstallError(String(error))).finally(() => setRemovingName(null));
6480
- }, [refreshInstalled]);
6553
+ }, [
6554
+ refreshInstalled,
6555
+ hotNames,
6556
+ refreshNames
6557
+ ]);
6481
6558
  /** Live enable/disable of one installed plugin (#60). `reload` opts the
6482
6559
  * card-level theme flow into a page refresh so the visual result lands
6483
6560
  * immediately (mirrors the use-skin reload on activate). */