@linxin666/dsh-client-ui-task-board 0.1.15 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -2274,7 +2274,7 @@ window.__ModuleLoader__.load({
2274
2274
  }
2275
2275
  //#endregion
2276
2276
  //#region \0dsh-css:packages/dsh-task-board/src/client/settings-card.module.css.mjs
2277
- const css = ".Jh0q7G_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.Jh0q7G_card:hover{border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.Jh0q7G_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Jh0q7G_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.Jh0q7G_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.Jh0q7G_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.Jh0q7G_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Jh0q7G_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.Jh0q7G_chevronOpen{transform:rotate(180deg)}.Jh0q7G_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.Jh0q7G_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.Jh0q7G_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.Jh0q7G_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.Jh0q7G_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}.Jh0q7G_discard,.Jh0q7G_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.Jh0q7G_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.Jh0q7G_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.Jh0q7G_discard:disabled,.Jh0q7G_save:disabled{opacity:.4;cursor:default}.Jh0q7G_discard:focus-visible,.Jh0q7G_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Jh0q7G_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.Jh0q7G_field+.Jh0q7G_field{border-top:1px solid var(--dsw-alias-border-l2)}.Jh0q7G_head{align-items:center;gap:8px;display:flex}.Jh0q7G_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.Jh0q7G_badges{align-items:center;gap:8px;display:inline-flex}.Jh0q7G_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Jh0q7G_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.Jh0q7G_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.Jh0q7G_reset:disabled{cursor:default}.Jh0q7G_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.Jh0q7G_input,.Jh0q7G_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Jh0q7G_input:focus-visible,.Jh0q7G_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.Jh0q7G_input:disabled,.Jh0q7G_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.Jh0q7G_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Jh0q7G_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.Jh0q7G_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.Jh0q7G_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.Jh0q7G_card,.Jh0q7G_header,.Jh0q7G_chevron,.Jh0q7G_chevronOpen,.Jh0q7G_discard,.Jh0q7G_save{transition:none}}";
2277
+ const css = ".Jh0q7G_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.Jh0q7G_card:hover{border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.Jh0q7G_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Jh0q7G_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.Jh0q7G_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.Jh0q7G_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.Jh0q7G_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Jh0q7G_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.Jh0q7G_chevronOpen{transform:rotate(180deg)}.Jh0q7G_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.Jh0q7G_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.Jh0q7G_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.Jh0q7G_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.Jh0q7G_failed{min-width:0;color:var(--dsw-alias-label-error);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:12px;line-height:1.5;overflow:hidden}.Jh0q7G_discard,.Jh0q7G_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.Jh0q7G_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.Jh0q7G_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.Jh0q7G_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.Jh0q7G_discard:disabled,.Jh0q7G_save:disabled{opacity:.4;cursor:default}.Jh0q7G_discard:focus-visible,.Jh0q7G_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Jh0q7G_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.Jh0q7G_field+.Jh0q7G_field{border-top:1px solid var(--dsw-alias-border-l2)}.Jh0q7G_head{align-items:center;gap:8px;display:flex}.Jh0q7G_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.Jh0q7G_badges{align-items:center;gap:8px;display:inline-flex}.Jh0q7G_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Jh0q7G_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.Jh0q7G_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.Jh0q7G_reset:disabled{cursor:default}.Jh0q7G_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.Jh0q7G_input,.Jh0q7G_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Jh0q7G_input:focus-visible,.Jh0q7G_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.Jh0q7G_input:disabled,.Jh0q7G_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.Jh0q7G_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Jh0q7G_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.Jh0q7G_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.Jh0q7G_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.Jh0q7G_card,.Jh0q7G_header,.Jh0q7G_chevron,.Jh0q7G_chevronOpen,.Jh0q7G_discard,.Jh0q7G_save{transition:none}}";
2278
2278
  const tagId = "@linxin666/dsh-client-ui-task-board/settings-card.module.css";
2279
2279
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
2280
2280
  const tag = document.createElement("style");
@@ -2430,10 +2430,10 @@ window.__ModuleLoader__.load({
2430
2430
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2431
2431
  className: settings_card_module_css_default.footer,
2432
2432
  children: [
2433
- state.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2433
+ state.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
2434
2434
  className: settings_card_module_css_default.failed,
2435
2435
  role: "status",
2436
- children: props.t("settings.saveFailed")
2436
+ children: [props.t("settings.saveFailed"), state.failedReason ? " - " + state.failedReason : ""]
2437
2437
  }) : null,
2438
2438
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2439
2439
  type: "button",
@@ -2546,6 +2546,7 @@ window.__ModuleLoader__.load({
2546
2546
  listeners = /* @__PURE__ */ new Set();
2547
2547
  saving = false;
2548
2548
  failed = false;
2549
+ failedReason;
2549
2550
  /** @param scope - the bound settings scope for this card's namespace. */
2550
2551
  constructor(scope, specs) {
2551
2552
  this.scope = scope;
@@ -2573,7 +2574,8 @@ window.__ModuleLoader__.load({
2573
2574
  dirty: plan.length > 0,
2574
2575
  invalid: plan.some((item) => item.run === void 0),
2575
2576
  saving: this.saving,
2576
- failed: this.failed
2577
+ failed: this.failed,
2578
+ ...this.failedReason === void 0 ? {} : { failedReason: this.failedReason }
2577
2579
  };
2578
2580
  }
2579
2581
  /** Read one field's state from the effective section and its staged draft. */
@@ -2614,29 +2616,50 @@ window.__ModuleLoader__.load({
2614
2616
  if (this.staged.size === 0 && !this.failed) return;
2615
2617
  this.staged.clear();
2616
2618
  this.failed = false;
2619
+ this.failedReason = void 0;
2617
2620
  this.publish();
2618
2621
  }
2619
2622
  };
2620
2623
  }
2621
2624
  /**
2622
2625
  * Write every staged edit, then re-seed from what the Host accepted.
2626
+ *
2627
+ * When the scope carries the optional batch surface (the dsh-web-ui
2628
+ * bridge scope), every planned write rides one mutation so cross-field
2629
+ * validate hooks (baseURL+model) judge the batch as a unit instead of
2630
+ * deadlocking on per-field writes. Otherwise the per-field loop runs.
2631
+ * A field lands only when the Host reports it held the staged value; a
2632
+ * landed field's draft is dropped, a failed one stays staged for the user.
2623
2633
  * @returns settlement after every write and the read-back.
2624
2634
  */
2625
2635
  async save() {
2626
2636
  const plan = this.plan();
2627
- const writes = plan.flatMap((item) => item.run === void 0 ? [] : [item.run]);
2628
- if (plan.length === 0 || this.saving || writes.length !== plan.length) return;
2637
+ const valid = plan.filter((item) => item.run !== void 0);
2638
+ if (plan.length === 0 || this.saving || valid.length !== plan.length) return;
2639
+ const plannedWrites = valid.map((item) => item.op);
2629
2640
  const fields = new Set(plan.map((item) => item.field));
2630
2641
  this.saving = true;
2631
2642
  this.failed = false;
2643
+ this.failedReason = void 0;
2632
2644
  this.publish();
2633
- let landed = true;
2634
- for (const write of writes) landed = await write() && landed;
2635
- if (landed) for (const field of fields) this.staged.delete(field);
2645
+ const landed = /* @__PURE__ */ new Set();
2646
+ const batch = this.batchedScope();
2647
+ if (batch !== void 0) {
2648
+ const result = await batch.mutate(plannedWrites);
2649
+ if (result.ok) {
2650
+ for (const field of result.fields) if (field.landed) landed.add(field.field);
2651
+ } else this.failedReason = result.message;
2652
+ } else for (const item of valid) if (await item.run()) landed.add(item.field);
2653
+ for (const field of fields) if (landed.has(field)) this.staged.delete(field);
2636
2654
  this.saving = false;
2637
- this.failed = !landed;
2655
+ this.failed = landed.size !== fields.size;
2638
2656
  this.publish();
2639
2657
  }
2658
+ /** The scope's batch surface when it supports one; undefined conservatively otherwise. */
2659
+ batchedScope() {
2660
+ const candidate = this.scope;
2661
+ return typeof candidate?.mutate === "function" ? candidate : void 0;
2662
+ }
2640
2663
  /**
2641
2664
  * Every staged edit a save would write. An entry whose draft is not a value
2642
2665
  * its field accepts carries no write: the form is still dirty, and the save
@@ -2651,6 +2674,10 @@ window.__ModuleLoader__.load({
2651
2674
  if (staged.clear) {
2652
2675
  if (this.stored(field)) plan.push({
2653
2676
  field,
2677
+ op: {
2678
+ field,
2679
+ op: "unset"
2680
+ },
2654
2681
  run: () => this.clear(field)
2655
2682
  });
2656
2683
  continue;
@@ -2659,14 +2686,27 @@ window.__ModuleLoader__.load({
2659
2686
  const write = spec.parse(staged.text);
2660
2687
  if (write === void 0) plan.push({
2661
2688
  field,
2689
+ op: {
2690
+ field,
2691
+ op: "unset"
2692
+ },
2662
2693
  run: void 0
2663
2694
  });
2664
2695
  else if (write.kind === "clear") plan.push({
2665
2696
  field,
2697
+ op: {
2698
+ field,
2699
+ op: "unset"
2700
+ },
2666
2701
  run: () => this.clear(field)
2667
2702
  });
2668
2703
  else plan.push({
2669
2704
  field,
2705
+ op: {
2706
+ field,
2707
+ op: "set",
2708
+ value: write.value
2709
+ },
2670
2710
  run: () => this.store(field, write.value)
2671
2711
  });
2672
2712
  }
@@ -2678,11 +2718,13 @@ window.__ModuleLoader__.load({
2678
2718
  }
2679
2719
  async store(field, value) {
2680
2720
  await this.scope.set(field, value);
2721
+ if (this.specOf(field).secret) return true;
2681
2722
  return this.userLayer()?.[field] === value;
2682
2723
  }
2683
2724
  stage(field, edit) {
2684
2725
  this.staged.set(field, edit);
2685
2726
  this.failed = false;
2727
+ this.failedReason = void 0;
2686
2728
  this.publish();
2687
2729
  }
2688
2730
  specOf(field) {