dsh-any-background 0.2.2 → 0.2.4

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
@@ -47,6 +47,13 @@ window.__ModuleLoader__.load({
47
47
  bgTitle: "背景",
48
48
  bgChoose: "选择图片或视频",
49
49
  bgRemove: "移除背景",
50
+ bgFromUrl: "从网址",
51
+ bgUrlPlaceholder: "粘贴图片网址 https://…",
52
+ bgUrlApply: "应用",
53
+ bgUrlCancel: "取消",
54
+ bgUrlApplying: "加载中…",
55
+ bgUrlBadHttp: "仅支持 http/https 图片网址",
56
+ bgUrlFail: "获取图片失败",
50
57
  bgEdit: "编辑位置",
51
58
  bgEditLocked: "仅「适应」模式可编辑位置",
52
59
  wpOpacity: "背景透明度",
@@ -145,6 +152,13 @@ window.__ModuleLoader__.load({
145
152
  bgTitle: "Background",
146
153
  bgChoose: "Choose image or video",
147
154
  bgRemove: "Remove background",
155
+ bgFromUrl: "From URL",
156
+ bgUrlPlaceholder: "Paste an image URL https://…",
157
+ bgUrlApply: "Apply",
158
+ bgUrlCancel: "Cancel",
159
+ bgUrlApplying: "Loading…",
160
+ bgUrlBadHttp: "Only http/https image URLs are supported",
161
+ bgUrlFail: "Could not fetch the image",
148
162
  bgEdit: "Edit position",
149
163
  bgEditLocked: "Position editing is only available in Fit mode",
150
164
  wpOpacity: "Background opacity",
@@ -556,6 +570,40 @@ window.__ModuleLoader__.load({
556
570
  async function persistVideo(dataUrl) {
557
571
  return await rpcCall("setVideo", { dataUrl }) === true;
558
572
  }
573
+ /** Download a wallpaper from a network URL and persist it into the local slot
574
+ * (the host replaces wallpaper.jpg). Returns the freshly stored data-URL on
575
+ * success, or the host's failure message. */
576
+ async function setWallpaperFromUrl(url) {
577
+ if (!rpcCallFn) return {
578
+ ok: false,
579
+ error: "rpc not ready"
580
+ };
581
+ let res;
582
+ try {
583
+ res = await rpcCallFn(rpcEndpoint("setWallpaperUrl"), { url });
584
+ } catch (e) {
585
+ return {
586
+ ok: false,
587
+ error: e instanceof Error ? e.message : String(e)
588
+ };
589
+ }
590
+ if (!res) return {
591
+ ok: false,
592
+ error: "no response"
593
+ };
594
+ if (res.ok !== true) return {
595
+ ok: false,
596
+ error: res.error?.message ?? "request failed"
597
+ };
598
+ const v = res.value;
599
+ return v?.ok === true ? {
600
+ ok: true,
601
+ dataUrl: v.dataUrl ?? null
602
+ } : {
603
+ ok: false,
604
+ error: v?.error ?? "failed"
605
+ };
606
+ }
559
607
  /** Upload a video's raw bytes over HTTP (MIME in Content-Type, body untouched
560
608
  * — no base64 inflation that would blow the RPC body limit on large clips). */
561
609
  async function uploadVideo(blob, mime) {
@@ -669,13 +717,13 @@ window.__ModuleLoader__.load({
669
717
  "--dsw-alias-state-error-secondary": "#ff8aa0",
670
718
  "--dsw-alias-state-success-primary": "#3ddc84",
671
719
  "--dsw-alias-state-success-secondary": "#69eea4",
672
- "--dsw-alias-state-success-tertiary": "hsl(152,70%,62%)",
720
+ "--dsw-alias-state-success-tertiary": "#233c2c",
673
721
  "--dsw-alias-state-warn-primary": "#ffb347",
674
722
  "--dsw-alias-state-warn-secondary": "#ffc980",
675
- "--dsw-alias-state-warn-tertiary": "#dd8629",
723
+ "--dsw-alias-state-warn-tertiary": "#27241f",
676
724
  "--dsw-alias-state-warn-label": "#dd8629",
677
725
  "--dsw-alias-state-business-primary": "#679efe",
678
- "--dsw-alias-state-business-tertiary": "#6f7fa8",
726
+ "--dsw-alias-state-business-tertiary": "#34415b",
679
727
  "--dsw-specific-sidebar-fill": hsl(h(0), s(0), l(-.06)),
680
728
  "--dsw-specific-sidebar-nav-item-active": hsl(h(0), s(0), l(.04)),
681
729
  "--dsw-specific-sidebar-nav-item-hover": hsl(h(0), s(0), l(0)),
@@ -1986,6 +2034,36 @@ window.__ModuleLoader__.load({
1986
2034
  viewTargets[i] = null;
1987
2035
  });
1988
2036
  }
2037
+ const TABLE_FIX_RULE = [
2038
+ ".md-table-wide {",
2039
+ " --dsh-table-spare: 0px !important;",
2040
+ " --dsh-table-lead: 0px !important;",
2041
+ " box-sizing: border-box !important;",
2042
+ " width: 100% !important;",
2043
+ " max-width: 100% !important;",
2044
+ " margin-left: 0 !important;",
2045
+ " padding-left: 0 !important;",
2046
+ " padding-bottom: 0 !important;",
2047
+ " overflow-x: auto !important;",
2048
+ "}"
2049
+ ].join("\n");
2050
+ let tableFixStyleEl = null;
2051
+ /** Toggle the wide-table clamp according to the chat region's opacity & blur. */
2052
+ function syncTableFix() {
2053
+ if (!(rChatTextOpacity() > 0 || rBlurs().chat > 0)) {
2054
+ if (tableFixStyleEl !== null) {
2055
+ tableFixStyleEl.remove();
2056
+ tableFixStyleEl = null;
2057
+ }
2058
+ return;
2059
+ }
2060
+ if (tableFixStyleEl === null) {
2061
+ tableFixStyleEl = document.createElement("style");
2062
+ tableFixStyleEl.dataset.plugin = "dsh-any-background-table-fix";
2063
+ tableFixStyleEl.textContent = TABLE_FIX_RULE;
2064
+ }
2065
+ if (!tableFixStyleEl.isConnected) document.head.appendChild(tableFixStyleEl);
2066
+ }
1989
2067
  /** Re-derive the conversation view cards from the current config. Cheap
1990
2068
  * enough for live slider drags; the card structure is applied unconditionally
1991
2069
  * once the host exists so the layout never reflows when a slider leaves zero. */
@@ -2011,6 +2089,7 @@ window.__ModuleLoader__.load({
2011
2089
  target.setAttribute(spec.mark, "1");
2012
2090
  setBlur(target, blurPx);
2013
2091
  });
2092
+ syncTableFix();
2014
2093
  }
2015
2094
  let partsObserver = null;
2016
2095
  /** Watch for the AppFrame mounting so persisted blurs land even when the shell
@@ -2329,6 +2408,8 @@ window.__ModuleLoader__.load({
2329
2408
  tokensRaf = null;
2330
2409
  }
2331
2410
  pendingOps = null;
2411
+ tableFixStyleEl?.remove();
2412
+ tableFixStyleEl = null;
2332
2413
  setBlur(frameEl, 0);
2333
2414
  setBlur(sidebarEl, 0);
2334
2415
  setBlur(centerEl, 0);
@@ -2475,6 +2556,9 @@ window.__ModuleLoader__.load({
2475
2556
  .dab-num:focus{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-brand-primary) 18%,transparent)}
2476
2557
  .dab-num::-webkit-outer-spin-button,.dab-num::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
2477
2558
  .dab-num{-moz-appearance:textfield;appearance:textfield}
2559
+ .dab-urlinput{flex:1;min-width:180px;height:34px;padding:0 12px;border-radius:10px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-size:12.5px;outline:none;transition:border-color .2s}
2560
+ .dab-urlinput::placeholder{color:var(--dsw-alias-label-quaternary)}
2561
+ .dab-urlinput:focus{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-brand-primary) 18%,transparent)}
2478
2562
  .dab-swatch-lg{height:38px;border-radius:10px;border:1px solid var(--dsw-alias-border-l2);box-shadow:inset 0 0 14px rgba(0,0,0,.1);transition:transform .3s ease}
2479
2563
  .dab-swatch-lg:hover{transform:scale(1.02)}
2480
2564
 
@@ -2774,6 +2858,15 @@ window.__ModuleLoader__.load({
2774
2858
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.8 13.8h10.4" })
2775
2859
  ]
2776
2860
  });
2861
+ const LinkIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
2862
+ size,
2863
+ className,
2864
+ children: [
2865
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M6.6 9.4 9.4 6.6" }),
2866
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M5.9 11.1l-1.4 1.4a2.6 2.6 0 0 1-3.7-3.7L3.5 6.8" }),
2867
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M10.1 4.9l1.4-1.4a2.6 2.6 0 0 1 3.7 3.7l-2.1 2.1" })
2868
+ ]
2869
+ });
2777
2870
  const LockIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
2778
2871
  size,
2779
2872
  className,
@@ -4090,7 +4183,7 @@ window.__ModuleLoader__.load({
4090
4183
  }),
4091
4184
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4092
4185
  type: "button",
4093
- className: "dab-btn dab-btn-primary",
4186
+ className: "dab-btn",
4094
4187
  onClick: () => onCommit(zoom, (pos.x + imgSize.w * zoom / 2) / pw, (pos.y + imgSize.h * zoom / 2) / ph, imgRef.current?.naturalWidth ?? 0, imgRef.current?.naturalHeight ?? 0),
4095
4188
  children: t("editorCommit")
4096
4189
  })
@@ -4135,6 +4228,10 @@ window.__ModuleLoader__.load({
4135
4228
  const [editorOpen, setEditorOpen] = (0, react.useState)(false);
4136
4229
  const [dragOver, setDragOver] = (0, react.useState)(false);
4137
4230
  const [spinTick, setSpinTick] = (0, react.useState)(0);
4231
+ const [urlOpen, setUrlOpen] = (0, react.useState)(false);
4232
+ const [urlVal, setUrlVal] = (0, react.useState)("");
4233
+ const [urlBusy, setUrlBusy] = (0, react.useState)(false);
4234
+ const [urlErr, setUrlErr] = (0, react.useState)(null);
4138
4235
  const [mode, setModeState] = (0, react.useState)(rBgMode());
4139
4236
  const isVideo = backgroundType === "video";
4140
4237
  const isStatic = backgroundType === "image" || isVideo;
@@ -4155,6 +4252,22 @@ window.__ModuleLoader__.load({
4155
4252
  const f = e.dataTransfer.files?.[0];
4156
4253
  if (f && (f.type.startsWith("image/") || f.type.startsWith("video/"))) onFileSelect(f);
4157
4254
  };
4255
+ const applyUrl = async () => {
4256
+ const u = urlVal.trim();
4257
+ if (!/^https?:\/\//i.test(u)) {
4258
+ setUrlErr(t("bgUrlBadHttp"));
4259
+ return;
4260
+ }
4261
+ setUrlBusy(true);
4262
+ setUrlErr(null);
4263
+ const r = await setWallpaperFromUrl(u);
4264
+ setUrlBusy(false);
4265
+ if (r.ok) {
4266
+ setWp(r.dataUrl ?? null);
4267
+ setUrlOpen(false);
4268
+ setUrlVal("");
4269
+ } else setUrlErr(r.error === "invalid url" || r.error === "unsupported scheme" ? t("bgUrlBadHttp") : r.error ?? t("bgUrlFail"));
4270
+ };
4158
4271
  const switchToStatic = () => {
4159
4272
  if (isStatic) return;
4160
4273
  setBgType(isVideo || cfg.videoMime !== null ? "video" : "image");
@@ -4315,41 +4428,105 @@ window.__ModuleLoader__.load({
4315
4428
  !isGenerated ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4316
4429
  className: "dab-card dab-card-hover dab-rise",
4317
4430
  style: { "--d": 3 },
4318
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4319
- className: "dab-chip-row",
4320
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4321
- type: "button",
4322
- className: "dab-btn dab-btn-primary",
4323
- onClick: () => fileRef.current?.click(),
4324
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UploadIcon, { size: 14 }), t("bgChoose")]
4325
- }), storeUrl || isVideo ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [isStatic && storeUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4326
- type: "button",
4327
- className: "dab-btn",
4328
- disabled: mode !== "fit",
4329
- title: mode !== "fit" ? t("bgEditLocked") : void 0,
4330
- onClick: () => setEditorOpen(true),
4331
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditIcon, { size: 14 }), t("bgEdit")]
4332
- }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4333
- type: "button",
4334
- className: "dab-btn dab-btn-ghost dab-btn-danger",
4335
- onClick: () => setWp(null),
4336
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, { size: 14 }), t("bgRemove")]
4337
- })] }) : null]
4338
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4339
- style: { marginTop: 16 },
4340
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4341
- className: "dab-swatch-title",
4342
- children: t("bgModeTitle")
4343
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4431
+ children: [
4432
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4344
4433
  className: "dab-chip-row",
4345
- children: BG_MODES.map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4346
- type: "button",
4347
- className: `dab-chip${mode === m.mode ? " is-active" : ""}`,
4348
- onClick: () => setMode(m.mode),
4349
- children: t(m.labelKey)
4350
- }, m.mode))
4351
- })]
4352
- })]
4434
+ children: [
4435
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4436
+ type: "button",
4437
+ className: "dab-btn dab-btn-primary",
4438
+ onClick: () => fileRef.current?.click(),
4439
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UploadIcon, { size: 14 }), t("bgChoose")]
4440
+ }),
4441
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4442
+ type: "button",
4443
+ className: "dab-btn dab-btn-ghost",
4444
+ onClick: () => setUrlOpen((o) => !o),
4445
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LinkIcon, { size: 14 }), t("bgFromUrl")]
4446
+ }),
4447
+ storeUrl || isVideo ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [isStatic && storeUrl ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4448
+ type: "button",
4449
+ className: "dab-btn",
4450
+ disabled: mode !== "fit",
4451
+ title: mode !== "fit" ? t("bgEditLocked") : void 0,
4452
+ onClick: () => setEditorOpen(true),
4453
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditIcon, { size: 14 }), t("bgEdit")]
4454
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4455
+ type: "button",
4456
+ className: "dab-btn dab-btn-ghost dab-btn-danger",
4457
+ onClick: () => setWp(null),
4458
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, { size: 14 }), t("bgRemove")]
4459
+ })] }) : null
4460
+ ]
4461
+ }),
4462
+ urlOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4463
+ className: "dab-urlrow",
4464
+ style: {
4465
+ marginTop: 12,
4466
+ display: "flex",
4467
+ flexWrap: "wrap",
4468
+ gap: 8,
4469
+ alignItems: "center"
4470
+ },
4471
+ children: [
4472
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
4473
+ type: "text",
4474
+ className: "dab-urlinput",
4475
+ value: urlVal,
4476
+ placeholder: t("bgUrlPlaceholder"),
4477
+ onChange: (e) => setUrlVal(e.target.value),
4478
+ onKeyDown: (e) => {
4479
+ if (e.key === "Enter") {
4480
+ e.preventDefault();
4481
+ applyUrl();
4482
+ }
4483
+ },
4484
+ autoFocus: true
4485
+ }),
4486
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4487
+ type: "button",
4488
+ className: "dab-btn dab-btn-primary",
4489
+ disabled: urlBusy,
4490
+ onClick: () => void applyUrl(),
4491
+ children: urlBusy ? t("bgUrlApplying") : t("bgUrlApply")
4492
+ }),
4493
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4494
+ type: "button",
4495
+ className: "dab-btn",
4496
+ onClick: () => {
4497
+ setUrlOpen(false);
4498
+ setUrlVal("");
4499
+ setUrlErr(null);
4500
+ },
4501
+ children: t("bgUrlCancel")
4502
+ })
4503
+ ]
4504
+ }) : null,
4505
+ urlErr ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4506
+ className: "dab-urlerr",
4507
+ style: {
4508
+ marginTop: 8,
4509
+ color: "var(--dsw-alias-state-error-primary)",
4510
+ fontSize: 12
4511
+ },
4512
+ children: urlErr
4513
+ }) : null,
4514
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4515
+ style: { marginTop: 16 },
4516
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4517
+ className: "dab-swatch-title",
4518
+ children: t("bgModeTitle")
4519
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4520
+ className: "dab-chip-row",
4521
+ children: BG_MODES.map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4522
+ type: "button",
4523
+ className: `dab-chip${mode === m.mode ? " is-active" : ""}`,
4524
+ onClick: () => setMode(m.mode),
4525
+ children: t(m.labelKey)
4526
+ }, m.mode))
4527
+ })]
4528
+ })
4529
+ ]
4353
4530
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4354
4531
  className: "dab-card dab-rise",
4355
4532
  style: { "--d": 3 },