dsh-cool-theme 0.6.1 → 0.7.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/lib/client.js CHANGED
@@ -28,6 +28,7 @@ window.__ModuleLoader__.load({
28
28
  //#endregion
29
29
  let react = require("react");
30
30
  react = __toESM(react, 1);
31
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
31
32
  //#region src/contract.ts
32
33
  /**
33
34
  * Shared contract for dsh-cool-theme.
@@ -39,6 +40,7 @@ window.__ModuleLoader__.load({
39
40
  const CUSTOM_LIST_STORAGE_KEY = "cool-theme-custom-list";
40
41
  const CUSTOM_ACTIVE_STORAGE_KEY = "cool-theme-custom-active";
41
42
  const CUSTOM_MIGRATED_STORAGE_KEY = "cool-theme-custom-migrated";
43
+ const SIDEBAR_TRANSPARENT_STORAGE_KEY = "cool-theme-sidebar-transparent";
42
44
  const THEME_API_PATH_THEMES = `/cool-theme/api/themes`;
43
45
  const THEME_ARCHIVE_EXTENSION = ".zip";
44
46
  /**
@@ -2301,6 +2303,432 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
2301
2303
  };
2302
2304
  }
2303
2305
  //#endregion
2306
+ //#region node_modules/.pnpm/react-colorful@5.8.1_react-dom@19.3.0_react@18.3.1__react@18.3.1/node_modules/react-colorful/dist/index.mjs
2307
+ function l() {
2308
+ return (l = Object.assign || function(e) {
2309
+ for (var r = 1; r < arguments.length; r++) {
2310
+ var n = arguments[r];
2311
+ for (var t in n) Object.prototype.hasOwnProperty.call(n, t) && (e[t] = n[t]);
2312
+ }
2313
+ return e;
2314
+ }).apply(this, arguments);
2315
+ }
2316
+ function c(e, r) {
2317
+ if (null == e) return {};
2318
+ var n, t, o = {}, a = Object.keys(e);
2319
+ for (t = 0; t < a.length; t++) r.indexOf(n = a[t]) >= 0 || (o[n] = e[n]);
2320
+ return o;
2321
+ }
2322
+ function i(e) {
2323
+ var n = (0, react.useRef)(e), t = (0, react.useRef)(function(e) {
2324
+ n.current && n.current(e);
2325
+ });
2326
+ return n.current = e, t.current;
2327
+ }
2328
+ var s = function(e, r, n) {
2329
+ return void 0 === r && (r = 0), void 0 === n && (n = 1), e > n ? n : e < r ? r : e;
2330
+ };
2331
+ var f = function(e) {
2332
+ return "touches" in e;
2333
+ };
2334
+ var d = function(e) {
2335
+ return e && e.ownerDocument.defaultView || self;
2336
+ };
2337
+ var v = function(e, r, n) {
2338
+ var t = e.getBoundingClientRect(), o = f(r) ? function(e, r) {
2339
+ for (var n = 0; n < e.length; n++) if (e[n].identifier === r) return e[n];
2340
+ return e[0];
2341
+ }(r.touches, n) : r;
2342
+ return {
2343
+ left: s((o.pageX - (t.left + d(e).pageXOffset)) / t.width),
2344
+ top: s((o.pageY - (t.top + d(e).pageYOffset)) / t.height)
2345
+ };
2346
+ };
2347
+ var h = function(e) {
2348
+ !f(e) && e.preventDefault();
2349
+ };
2350
+ var g = react.default.memo(function(o) {
2351
+ var a = o.onMove, u = o.onKey, s = o.onEnd, g = c(o, [
2352
+ "onMove",
2353
+ "onKey",
2354
+ "onEnd"
2355
+ ]), m = (0, react.useRef)(null), p = i(a), b = i(u), _ = i(s), E = (0, react.useRef)(null), C = (0, react.useRef)(!1), x = (0, react.useMemo)(function() {
2356
+ var e = function(e) {
2357
+ h(e), (f(e) ? e.touches.length > 0 : e.buttons > 0) && m.current ? p(v(m.current, e, E.current)) : (n(!1), _());
2358
+ }, r = function() {
2359
+ n(!1), _();
2360
+ };
2361
+ function n(n) {
2362
+ var t = C.current, o = d(m.current), a = n ? o.addEventListener : o.removeEventListener;
2363
+ a(t ? "touchmove" : "mousemove", e), a(t ? "touchend" : "mouseup", r);
2364
+ }
2365
+ return [
2366
+ function(e) {
2367
+ var r = e.nativeEvent, t = m.current;
2368
+ if (t && (h(r), !function(e, r) {
2369
+ return r && !f(e);
2370
+ }(r, C.current) && t)) {
2371
+ if (f(r)) {
2372
+ C.current = !0;
2373
+ var o = r.changedTouches || [];
2374
+ o.length && (E.current = o[0].identifier);
2375
+ }
2376
+ t.focus(), p(v(t, r, E.current)), n(!0);
2377
+ }
2378
+ },
2379
+ function(e) {
2380
+ var r = e.which || e.keyCode;
2381
+ r < 37 || r > 40 || (e.preventDefault(), b({
2382
+ left: 39 === r ? .05 : 37 === r ? -.05 : 0,
2383
+ top: 40 === r ? .05 : 38 === r ? -.05 : 0
2384
+ }));
2385
+ },
2386
+ function(e) {
2387
+ var r = e.which || e.keyCode;
2388
+ r >= 37 && r <= 40 && _();
2389
+ },
2390
+ n
2391
+ ];
2392
+ }, [
2393
+ b,
2394
+ p,
2395
+ _
2396
+ ]), H = x[0], M = x[1], N = x[2], w = x[3];
2397
+ return (0, react.useEffect)(function() {
2398
+ return w;
2399
+ }, [w]), react.default.createElement("div", l({}, g, {
2400
+ onTouchStart: H,
2401
+ onMouseDown: H,
2402
+ className: "react-colorful__interactive",
2403
+ ref: m,
2404
+ onKeyDown: M,
2405
+ onKeyUp: N,
2406
+ tabIndex: 0,
2407
+ role: "slider"
2408
+ }));
2409
+ });
2410
+ var m = function(e) {
2411
+ return e.filter(Boolean).join(" ");
2412
+ };
2413
+ var p = function(r) {
2414
+ var n = r.color, t = r.left, o = r.top, a = void 0 === o ? .5 : o, u = m(["react-colorful__pointer", r.className]);
2415
+ return react.default.createElement("div", {
2416
+ className: u,
2417
+ style: {
2418
+ top: 100 * a + "%",
2419
+ left: 100 * t + "%"
2420
+ }
2421
+ }, react.default.createElement("div", {
2422
+ className: "react-colorful__pointer-fill",
2423
+ style: { backgroundColor: n }
2424
+ }));
2425
+ };
2426
+ var b = function(e, r, n) {
2427
+ return void 0 === r && (r = 0), void 0 === n && (n = Math.pow(10, r)), Math.round(n * e) / n;
2428
+ };
2429
+ 360 / (2 * Math.PI);
2430
+ var E = function(e) {
2431
+ return L(C(e));
2432
+ };
2433
+ var C = function(e) {
2434
+ return "#" === e[0] && (e = e.substring(1)), e.length < 6 ? {
2435
+ r: parseInt(e[0] + e[0], 16),
2436
+ g: parseInt(e[1] + e[1], 16),
2437
+ b: parseInt(e[2] + e[2], 16),
2438
+ a: 4 === e.length ? b(parseInt(e[3] + e[3], 16) / 255, 2) : 1
2439
+ } : {
2440
+ r: parseInt(e.substring(0, 2), 16),
2441
+ g: parseInt(e.substring(2, 4), 16),
2442
+ b: parseInt(e.substring(4, 6), 16),
2443
+ a: 8 === e.length ? b(parseInt(e.substring(6, 8), 16) / 255, 2) : 1
2444
+ };
2445
+ };
2446
+ var w = function(e) {
2447
+ return B(I(e));
2448
+ };
2449
+ var y = function(e) {
2450
+ var r = e.s, n = e.v, t = e.a, o = (200 - r) * n / 100;
2451
+ return {
2452
+ h: b(e.h),
2453
+ s: b(o > 0 && o < 200 ? r * n / 100 / (o <= 100 ? o : 200 - o) * 100 : 0),
2454
+ l: b(o / 2),
2455
+ a: b(t, 2)
2456
+ };
2457
+ };
2458
+ var k = function(e) {
2459
+ var r = y(e);
2460
+ return "hsl(" + r.h + ", " + r.s + "%, " + r.l + "%)";
2461
+ };
2462
+ var I = function(e) {
2463
+ var r = e.h, n = e.s, t = e.v, o = e.a;
2464
+ r = r / 360 * 6, n /= 100, t /= 100;
2465
+ var a = Math.floor(r), u = t * (1 - n), l = t * (1 - (r - a) * n), c = t * (1 - (1 - r + a) * n), i = a % 6;
2466
+ return {
2467
+ r: b(255 * [
2468
+ t,
2469
+ l,
2470
+ u,
2471
+ u,
2472
+ c,
2473
+ t
2474
+ ][i]),
2475
+ g: b(255 * [
2476
+ c,
2477
+ t,
2478
+ t,
2479
+ l,
2480
+ u,
2481
+ u
2482
+ ][i]),
2483
+ b: b(255 * [
2484
+ u,
2485
+ u,
2486
+ c,
2487
+ t,
2488
+ t,
2489
+ l
2490
+ ][i]),
2491
+ a: b(o, 2)
2492
+ };
2493
+ };
2494
+ var K = function(e) {
2495
+ var r = e.toString(16);
2496
+ return r.length < 2 ? "0" + r : r;
2497
+ };
2498
+ var B = function(e) {
2499
+ var r = e.r, n = e.g, t = e.b, o = e.a, a = o < 1 ? K(b(255 * o)) : "";
2500
+ return "#" + K(r) + K(n) + K(t) + a;
2501
+ };
2502
+ var L = function(e) {
2503
+ var r = e.r, n = e.g, t = e.b, o = e.a, a = Math.max(r, n, t), u = a - Math.min(r, n, t), l = u ? a === r ? (n - t) / u : a === n ? 2 + (t - r) / u : 4 + (r - n) / u : 0;
2504
+ return {
2505
+ h: 60 * (l < 0 ? l + 6 : l),
2506
+ s: a ? u / a * 100 : 0,
2507
+ v: a / 255 * 100,
2508
+ a: o
2509
+ };
2510
+ };
2511
+ var R = react.default.memo(function(r) {
2512
+ var n = r.hue, t = r.onChange, o = r.onChangeEnd, a = m(["react-colorful__hue", r.className]);
2513
+ return react.default.createElement("div", { className: a }, react.default.createElement(g, {
2514
+ onMove: function(e) {
2515
+ t({ h: 360 * e.left });
2516
+ },
2517
+ onKey: function(e) {
2518
+ t({ h: s(n + 360 * e.left, 0, 360) });
2519
+ },
2520
+ onEnd: o,
2521
+ "aria-label": "Hue",
2522
+ "aria-valuenow": b(n),
2523
+ "aria-valuemax": "360",
2524
+ "aria-valuemin": "0"
2525
+ }, react.default.createElement(p, {
2526
+ className: "react-colorful__hue-pointer",
2527
+ left: n / 360,
2528
+ color: k({
2529
+ h: n,
2530
+ s: 100,
2531
+ v: 100,
2532
+ a: 1
2533
+ })
2534
+ })));
2535
+ });
2536
+ var S = react.default.memo(function(r) {
2537
+ var n = r.hsva, t = r.onChange, o = r.onChangeEnd, a = { backgroundColor: k({
2538
+ h: n.h,
2539
+ s: 100,
2540
+ v: 100,
2541
+ a: 1
2542
+ }) };
2543
+ return react.default.createElement("div", {
2544
+ className: "react-colorful__saturation",
2545
+ style: a
2546
+ }, react.default.createElement(g, {
2547
+ onMove: function(e) {
2548
+ t({
2549
+ s: 100 * e.left,
2550
+ v: 100 - 100 * e.top
2551
+ });
2552
+ },
2553
+ onKey: function(e) {
2554
+ t({
2555
+ s: s(n.s + 100 * e.left, 0, 100),
2556
+ v: s(n.v - 100 * e.top, 0, 100)
2557
+ });
2558
+ },
2559
+ onEnd: o,
2560
+ "aria-label": "Color",
2561
+ "aria-valuetext": "Saturation " + b(n.s) + "%, Brightness " + b(n.v) + "%"
2562
+ }, react.default.createElement(p, {
2563
+ className: "react-colorful__saturation-pointer",
2564
+ top: 1 - n.v / 100,
2565
+ left: n.s / 100,
2566
+ color: k(n)
2567
+ })));
2568
+ });
2569
+ var T = function(e, r) {
2570
+ if (e === r) return !0;
2571
+ for (var n in e) if (e[n] !== r[n]) return !1;
2572
+ return !0;
2573
+ };
2574
+ var P = function(e, r) {
2575
+ return e.toLowerCase() === r.toLowerCase() || T(C(e), C(r));
2576
+ };
2577
+ function X(e, n, u, l) {
2578
+ var c = i(u), s = i(l), f = (0, react.useState)(function() {
2579
+ return e.toHsva(n);
2580
+ }), d = f[0], v = f[1], h = (0, react.useRef)({
2581
+ color: n,
2582
+ hsva: d
2583
+ }), g = (0, react.useRef)(!1);
2584
+ (0, react.useEffect)(function() {
2585
+ if (!e.equal(n, h.current.color)) {
2586
+ var r = e.toHsva(n);
2587
+ h.current = {
2588
+ hsva: r,
2589
+ color: n
2590
+ }, v(r), g.current = !1;
2591
+ }
2592
+ }, [n, e]), (0, react.useEffect)(function() {
2593
+ var r;
2594
+ T(d, h.current.hsva) || e.equal(r = e.fromHsva(d), h.current.color) || (h.current = {
2595
+ hsva: d,
2596
+ color: r
2597
+ }, c(r), g.current = !0);
2598
+ }, [
2599
+ d,
2600
+ e,
2601
+ c
2602
+ ]);
2603
+ return [
2604
+ d,
2605
+ (0, react.useCallback)(function(e) {
2606
+ v(function(r) {
2607
+ return Object.assign({}, r, e);
2608
+ });
2609
+ }, []),
2610
+ (0, react.useCallback)(function() {
2611
+ g.current && (g.current = !1, s(h.current.color));
2612
+ }, [s])
2613
+ ];
2614
+ }
2615
+ var Y;
2616
+ var U = "undefined" != typeof window ? react.useLayoutEffect : react.useEffect;
2617
+ var V = function() {
2618
+ return Y || ("undefined" != typeof __webpack_nonce__ ? __webpack_nonce__ : void 0);
2619
+ };
2620
+ var $ = /* @__PURE__ */ new WeakMap();
2621
+ var G = function(e) {
2622
+ U(function() {
2623
+ var r = e.current;
2624
+ if ("undefined" != typeof document && r) {
2625
+ var n = r.getRootNode ? r.getRootNode() : r.ownerDocument, t = n && ("head" in n || "host" in n) ? n : r.ownerDocument;
2626
+ if (!$.has(t)) {
2627
+ var o = "head" in t ? t.head : t, a = (o.ownerDocument || document).createElement("style");
2628
+ a.innerHTML = ".react-colorful{position:relative;display:flex;flex-direction:column;width:200px;height:200px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default}.react-colorful__saturation{position:relative;flex-grow:1;border-color:transparent;border-bottom:12px solid #000;border-radius:8px 8px 0 0;background-image:linear-gradient(0deg,#000,transparent),linear-gradient(90deg,#fff,hsla(0,0%,100%,0))}.react-colorful__alpha-gradient,.react-colorful__pointer-fill{content:\"\";position:absolute;left:0;top:0;right:0;bottom:0;pointer-events:none;border-radius:inherit}.react-colorful__alpha-gradient,.react-colorful__saturation{box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}.react-colorful__alpha,.react-colorful__hue{position:relative;height:24px}.react-colorful__hue{background:linear-gradient(90deg,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red)}.react-colorful__last-control{border-radius:0 0 8px 8px}.react-colorful__interactive{position:absolute;left:0;top:0;right:0;bottom:0;border-radius:inherit;outline:none;touch-action:none}.react-colorful__pointer{position:absolute;z-index:1;box-sizing:border-box;width:28px;height:28px;transform:translate(-50%,-50%);background-color:#fff;border:2px solid #fff;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.2)}.react-colorful__interactive:focus .react-colorful__pointer{transform:translate(-50%,-50%) scale(1.1)}.react-colorful__alpha,.react-colorful__alpha-pointer{background-color:#fff;background-image:url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill-opacity=\".05\"><path d=\"M8 0h8v8H8zM0 8h8v8H0z\"/></svg>')}.react-colorful__saturation-pointer{z-index:3}.react-colorful__hue-pointer{z-index:2}";
2629
+ var u = V();
2630
+ u && a.setAttribute("nonce", u), $.set(t, a), o.appendChild(a);
2631
+ }
2632
+ }
2633
+ }, []);
2634
+ };
2635
+ var J = function(n) {
2636
+ var t = n.className, o = n.colorModel, a = n.color, u = void 0 === a ? o.defaultColor : a, i = n.onChange, s = n.onChangeEnd, f = c(n, [
2637
+ "className",
2638
+ "colorModel",
2639
+ "color",
2640
+ "onChange",
2641
+ "onChangeEnd"
2642
+ ]), d = (0, react.useRef)(null);
2643
+ G(d);
2644
+ var v = X(o, u, i, s), h = v[0], g = v[1], p = v[2], b = m(["react-colorful", t]);
2645
+ return react.default.createElement("div", l({}, f, {
2646
+ ref: d,
2647
+ className: b
2648
+ }), react.default.createElement(S, {
2649
+ hsva: h,
2650
+ onChange: g,
2651
+ onChangeEnd: p
2652
+ }), react.default.createElement(R, {
2653
+ hue: h.h,
2654
+ onChange: g,
2655
+ onChangeEnd: p,
2656
+ className: "react-colorful__last-control"
2657
+ }));
2658
+ };
2659
+ var Q = {
2660
+ defaultColor: "000",
2661
+ toHsva: E,
2662
+ fromHsva: function(e) {
2663
+ return w({
2664
+ h: e.h,
2665
+ s: e.s,
2666
+ v: e.v,
2667
+ a: 1
2668
+ });
2669
+ },
2670
+ equal: P
2671
+ };
2672
+ var Z = function(r) {
2673
+ return react.default.createElement(J, l({}, r, { colorModel: Q }));
2674
+ };
2675
+ var ke = /^#?([0-9A-F]{3,8})$/i;
2676
+ var qe = function(r) {
2677
+ var n = r.color, u = void 0 === n ? "" : n, s = r.onChange, f = r.onBlur, d = r.escape, v = r.validate, h = r.format, g = r.process, m = c(r, [
2678
+ "color",
2679
+ "onChange",
2680
+ "onBlur",
2681
+ "escape",
2682
+ "validate",
2683
+ "format",
2684
+ "process"
2685
+ ]), p = (0, react.useState)(function() {
2686
+ return d(u);
2687
+ }), b = p[0], _ = p[1], E = i(s), C = i(f), x = (0, react.useCallback)(function(e) {
2688
+ var r = d(e.target.value);
2689
+ _(r), v(r) && E(g ? g(r) : r);
2690
+ }, [
2691
+ d,
2692
+ g,
2693
+ v,
2694
+ E
2695
+ ]), H = (0, react.useCallback)(function(e) {
2696
+ v(e.target.value) || _(d(u)), C(e);
2697
+ }, [
2698
+ u,
2699
+ d,
2700
+ v,
2701
+ C
2702
+ ]);
2703
+ return (0, react.useEffect)(function() {
2704
+ _(d(u));
2705
+ }, [u, d]), react.default.createElement("input", l({}, m, {
2706
+ value: h ? h(b) : b,
2707
+ spellCheck: "false",
2708
+ onChange: x,
2709
+ onBlur: H
2710
+ }));
2711
+ };
2712
+ var Ie = function(e) {
2713
+ return "#" + e;
2714
+ };
2715
+ var Oe = function(r) {
2716
+ var n = r.prefixed, t = r.alpha, o = c(r, ["prefixed", "alpha"]), u = (0, react.useCallback)(function(e) {
2717
+ return e.replace(/([^0-9A-F]+)/gi, "").substring(0, t ? 8 : 6);
2718
+ }, [t]), i = (0, react.useCallback)(function(e) {
2719
+ return function(e, r) {
2720
+ var n = ke.exec(e), t = n ? n[1].length : 0;
2721
+ return 3 === t || 6 === t || !!r && 4 === t || !!r && 8 === t;
2722
+ }(e, t);
2723
+ }, [t]);
2724
+ return react.default.createElement(qe, l({}, o, {
2725
+ escape: u,
2726
+ format: n ? Ie : void 0,
2727
+ process: Ie,
2728
+ validate: i
2729
+ }));
2730
+ };
2731
+ //#endregion
2304
2732
  //#region src/client/custom.ts
2305
2733
  /** The nine `--shiki-token-*` slots, in stylesheet order. */
2306
2734
  const SHIKI_KEYS = [
@@ -2809,6 +3237,22 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
2809
3237
  /** Reserved menu value that switches to the user-defined theme. */
2810
3238
  const CUSTOM_SELECTION = CUSTOM_PRESET_ID;
2811
3239
  /**
3240
+ * Base id of the light/dark control in the colour editor — and the only place
3241
+ * that string is written. The shell's `SegmentedControl` derives both the tab
3242
+ * ids (`<id>-<value>`) and the panel ids (`<id>-<value>-panel`) from it, so the
3243
+ * tabpanel that answers them has to be built from the same value: a second
3244
+ * literal would drift silently and leave `aria-controls` pointing at nothing.
3245
+ */
3246
+ const SEED_MODE_CONTROL_ID = "ct-seed-mode";
3247
+ /**
3248
+ * The one glyph size this panel draws at. Every icon here is explicit about it
3249
+ * because the shell's artwork does not default uniformly — the chevron falls
3250
+ * back to 14, the edit/share/trash/add/import glyphs to 16 — so passing no size
3251
+ * silently picks a different scale per icon. 14 is what the shell's own
3252
+ * settings pages pass.
3253
+ */
3254
+ const ICON_SIZE = 14;
3255
+ /**
2812
3256
  * Which message an import failure deserves. The Host answers with its own
2813
3257
  * reason, so the shape of that reason is what selects the localized text; an
2814
3258
  * unrecognised failure keeps the Host's own words rather than hiding them.
@@ -2854,114 +3298,54 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
2854
3298
  if (open.id === null) return true;
2855
3299
  return open.name.trim() !== open.originName.trim() || !sameSeeds(current, open.originTheme);
2856
3300
  }
2857
- function IconChevron() {
2858
- return react.createElement("svg", {
2859
- width: 14,
2860
- height: 14,
2861
- viewBox: "0 0 14 14",
2862
- fill: "none",
2863
- xmlns: "http://www.w3.org/2000/svg"
2864
- }, react.createElement("path", {
2865
- d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z",
2866
- fill: "currentColor"
2867
- }));
2868
- }
2869
- /** The menu's selected-row tick: 16px, pushed to the trailing edge. */
2870
- function IconCheck(props) {
2871
- const { size = 16, className } = props;
2872
- return react.createElement("svg", {
2873
- width: size,
2874
- height: size,
2875
- viewBox: "0 0 16 16",
2876
- fill: "none",
2877
- xmlns: "http://www.w3.org/2000/svg",
2878
- className
2879
- }, react.createElement("path", {
2880
- d: "M15.0498 3.92579L8.49512 12.3818C8.25774 12.6881 8.04517 12.9645 7.84668 13.1689C7.63957 13.3823 7.38732 13.5841 7.04492 13.6719C6.86373 13.7183 6.6757 13.7346 6.48926 13.7197C6.13666 13.6915 5.8528 13.5355 5.6123 13.3604C5.38201 13.1926 5.12573 12.9567 4.83984 12.6953L1.03125 9.21289L1.96875 8.1875L5.77734 11.6699C6.08684 11.9529 6.27773 12.1249 6.43066 12.2363C6.50183 12.2882 6.54699 12.3135 6.57324 12.3252C6.58525 12.3305 6.59269 12.3322 6.5957 12.333C6.59802 12.3336 6.59961 12.334 6.59961 12.334C6.63317 12.3367 6.66758 12.3335 6.7002 12.3252C6.7002 12.3252 6.70211 12.3251 6.7041 12.3242C6.70698 12.3229 6.71348 12.319 6.72461 12.3115C6.74849 12.2956 6.78843 12.2642 6.84961 12.2012C6.98138 12.0654 7.13957 11.8628 7.39648 11.5313L13.9502 3.07422L15.0498 3.92579Z",
2881
- fill: "currentColor"
2882
- }));
2883
- }
3301
+ /**
3302
+ * The panel's value picker: a trigger in the row, the shell's `Menu` for the
3303
+ * list. Everything the list needs — outside pointerdown, Escape, the arrow
3304
+ * walk, the focus return to the trigger, the translucent material, the
3305
+ * viewport-clamped portal — belongs to the primitive, so this owns `open` and
3306
+ * which label the trigger shows, and nothing else.
3307
+ */
2884
3308
  function SchemeMenu(props) {
2885
3309
  const { value, options, onSelect, disabled } = props;
2886
3310
  const [open, setOpen] = react.useState(false);
2887
- const rootRef = react.useRef(null);
2888
- const listRef = react.useRef(null);
2889
- react.useEffect(() => {
2890
- if (!open) return;
2891
- function onDown(e) {
2892
- if (!(e.target instanceof Node)) return;
2893
- if (rootRef.current?.contains(e.target)) return;
2894
- if (listRef.current?.contains(e.target)) return;
2895
- setOpen(false);
2896
- }
2897
- function onKey(e) {
2898
- if (e.key === "Escape") setOpen(false);
2899
- }
2900
- document.addEventListener("pointerdown", onDown);
2901
- document.addEventListener("keydown", onKey);
2902
- return () => {
2903
- document.removeEventListener("pointerdown", onDown);
2904
- document.removeEventListener("keydown", onKey);
2905
- };
2906
- }, [open]);
2907
3311
  const selected = options.find((o) => o.value === value);
2908
- return react.createElement("span", {
2909
- ref: rootRef,
2910
- style: {
2911
- position: "relative",
2912
- display: "inline-flex"
2913
- }
2914
- }, react.createElement("button", {
2915
- type: "button",
2916
- className: "ct-select",
2917
- disabled,
2918
- "aria-haspopup": "menu",
2919
- "aria-expanded": open && !disabled,
2920
- onClick: () => setOpen(!open)
2921
- }, react.createElement("span", { className: "ct-select-label" }, selected?.label ?? ""), react.createElement("span", { className: "ct-select-chevron" }, react.createElement(IconChevron, null))), open && !disabled ? react.createElement("div", {
2922
- ref: listRef,
2923
- className: "ct-menu-list",
2924
- style: {
2925
- position: "absolute",
2926
- top: "calc(100% + 4px)",
2927
- right: 0,
2928
- zIndex: 100
3312
+ const expanded = open && !disabled;
3313
+ return react.createElement(_deepseek_ai_dsh_client_ui_primitives.Menu, {
3314
+ open: expanded,
3315
+ align: "end",
3316
+ side: "bottom",
3317
+ portal: true,
3318
+ items: options.map((o) => ({
3319
+ id: o.value,
3320
+ label: o.label
3321
+ })),
3322
+ selectedId: value,
3323
+ onSelect: (v) => {
3324
+ setOpen(false);
3325
+ onSelect(v);
2929
3326
  },
2930
- role: "menu",
2931
- onClick: (e) => e.stopPropagation()
2932
- }, ...options.map((o) => {
2933
- const sel = o.value === value;
2934
- return react.createElement("button", {
2935
- key: o.value,
3327
+ onClose: () => setOpen(false),
3328
+ anchor: react.createElement("button", {
2936
3329
  type: "button",
2937
- role: "menuitem",
2938
- className: "ct-menu-item",
2939
- onClick: () => {
2940
- setOpen(false);
2941
- onSelect(o.value);
2942
- }
2943
- }, react.createElement("span", { className: "ct-menu-item-label" }, o.label), sel ? react.createElement(IconCheck, { className: "ct-menu-check" }) : null);
2944
- })) : null);
2945
- }
2946
- /** Accessible on/off switch used to enable the custom theme. */
2947
- function Switch(props) {
2948
- const { checked, label, onChange } = props;
2949
- return react.createElement("button", {
2950
- type: "button",
2951
- role: "switch",
2952
- "aria-checked": checked,
2953
- "aria-label": label,
2954
- className: checked ? "ct-switch ct-switch-on" : "ct-switch",
2955
- onClick: () => onChange(!checked)
2956
- }, react.createElement("span", { className: "ct-switch-knob" }));
3330
+ className: "ct-select",
3331
+ disabled,
3332
+ "aria-haspopup": "menu",
3333
+ "aria-expanded": expanded,
3334
+ onClick: () => setOpen(!open)
3335
+ }, react.createElement("span", { className: "ct-select-label" }, selected?.label ?? ""), react.createElement("span", { className: "ct-select-chevron" }, react.createElement(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { size: ICON_SIZE })))
3336
+ });
2957
3337
  }
2958
3338
  /**
2959
3339
  * Confirmation dialog for destructive actions, mirroring DSH's own Modal
2960
- * (design-platform mask token + blur, r24 card on layer-2, prominent shadow).
2961
- * Rendered inline rather than through a portal so it needs no react-dom import.
3340
+ * (design-platform mask token + blur, `radius-panel` card on layer-2,
3341
+ * prominent shadow) and the footer its `RiskConfirmation` lays out: an outline
3342
+ * cancel beside the confirming action.
3343
+ * Still rendered inline rather than through the shell's `Modal`: its Escape has
3344
+ * to take part in the panel's own leave-the-draft questions, and a shell-owned
3345
+ * modal layer would answer the key before this dialog could.
2962
3346
  */
2963
3347
  function ConfirmDialog(props) {
2964
- const { title, description, confirmLabel, cancelLabel, onConfirm, onCancel } = props;
3348
+ const { title, description, confirmLabel, cancelLabel, danger, onConfirm, onCancel } = props;
2965
3349
  react.useEffect(() => {
2966
3350
  const onKey = (e) => {
2967
3351
  if (e.key === "Escape") onCancel();
@@ -2978,13 +3362,13 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
2978
3362
  role: "dialog",
2979
3363
  "aria-modal": true,
2980
3364
  "aria-label": title
2981
- }, react.createElement("div", { className: "ct-modal-title" }, title), react.createElement("div", { className: "ct-modal-desc" }, description), react.createElement("div", { className: "ct-modal-footer" }, react.createElement("button", {
2982
- type: "button",
2983
- className: "ct-btn",
3365
+ }, react.createElement("div", { className: "ct-modal-title" }, title), react.createElement("div", { className: "ct-modal-desc" }, description), react.createElement("div", { className: "ct-modal-footer" }, react.createElement(_deepseek_ai_dsh_client_ui_primitives.Button, {
3366
+ variant: "outline",
3367
+ className: "ct-action-cancel",
2984
3368
  onClick: onCancel
2985
- }, cancelLabel), react.createElement("button", {
2986
- type: "button",
2987
- className: "ct-btn ct-btn-danger",
3369
+ }, cancelLabel), react.createElement(_deepseek_ai_dsh_client_ui_primitives.Button, {
3370
+ variant: danger ? "outline" : "primary",
3371
+ className: danger ? "ct-action-confirm ct-danger" : "ct-action-confirm",
2988
3372
  onClick: onConfirm
2989
3373
  }, confirmLabel))));
2990
3374
  }
@@ -2993,148 +3377,6 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
2993
3377
  return error instanceof ThemeApiUnavailableError ? "custom.toast.unavailable" : "custom.toast.failed";
2994
3378
  }
2995
3379
  /**
2996
- * Transient confirmation banner: a leading result badge plus the copy, top
2997
- * center, sliding in, holding, fading out, then reporting done so the owner can
2998
- * unmount it. Metrics, tokens, badge and timing follow DSH's own toast — the
2999
- * badge is the success circle its confirmations wear — but the component is
3000
- * local: the plugin must keep answering a save on a host that does not share
3001
- * the primitives module.
3002
- *
3003
- * TOAST_HOLD_MS and TOAST_FADE_MS have to agree with the `ct-toast` animation
3004
- * in the injected stylesheet, which owns the visual timing.
3005
- * @param props.text - resolved, already-localized copy.
3006
- * @param props.onDone - called once the fade completes; unmount the toast here.
3007
- */
3008
- function Toast(props) {
3009
- const { text, onDone } = props;
3010
- react.useEffect(() => {
3011
- const timer = setTimeout(onDone, 4e3);
3012
- return () => clearTimeout(timer);
3013
- }, [onDone]);
3014
- return react.createElement("div", {
3015
- className: "ct-toast",
3016
- role: "status"
3017
- }, react.createElement(ToastBadge), react.createElement("span", { className: "ct-toast-text" }, text));
3018
- }
3019
- /** The leading badge every confirmation wears: a success ring around the check. */
3020
- function ToastBadge() {
3021
- return react.createElement("span", {
3022
- className: "ct-toast-icon",
3023
- "aria-hidden": true
3024
- }, react.createElement(IconCheck, { size: 12 }));
3025
- }
3026
- /**
3027
- * Plus glyph for the add button, path data taken verbatim from DSH's own
3028
- * `IconPlusOutline16` so the two controls read as the same affordance.
3029
- */
3030
- function IconPlus(props) {
3031
- const { size = 14 } = props;
3032
- return react.createElement("svg", {
3033
- width: size,
3034
- height: size,
3035
- viewBox: "0 0 16 16",
3036
- fill: "none",
3037
- xmlns: "http://www.w3.org/2000/svg",
3038
- "aria-hidden": true,
3039
- focusable: false
3040
- }, react.createElement("path", {
3041
- d: "M8.64453 1.5V7.34961H14.5V8.65039H8.64453V14.5H7.34473V8.65039H1.5V7.34961H7.34473V1.5H8.64453Z",
3042
- fill: "currentColor"
3043
- }));
3044
- }
3045
- /**
3046
- * Lucide `edit` (the pencil in a square), path data verbatim. Row actions are
3047
- * icon-only, so the glyph has to read as the label the accessible name still
3048
- * carries.
3049
- */
3050
- function IconEdit(props) {
3051
- const { size = 14 } = props;
3052
- return react.createElement("svg", {
3053
- width: size,
3054
- height: size,
3055
- viewBox: "0 0 24 24",
3056
- fill: "none",
3057
- stroke: "currentColor",
3058
- strokeWidth: 2,
3059
- strokeLinecap: "round",
3060
- strokeLinejoin: "round",
3061
- xmlns: "http://www.w3.org/2000/svg",
3062
- "aria-hidden": true,
3063
- focusable: false
3064
- }, react.createElement("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }), react.createElement("path", { d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" }));
3065
- }
3066
- /** Lucide `trash`, path data verbatim; the row's danger action. */
3067
- function IconTrash(props) {
3068
- const { size = 14 } = props;
3069
- return react.createElement("svg", {
3070
- width: size,
3071
- height: size,
3072
- viewBox: "0 0 24 24",
3073
- fill: "none",
3074
- stroke: "currentColor",
3075
- strokeWidth: 2,
3076
- strokeLinecap: "round",
3077
- strokeLinejoin: "round",
3078
- xmlns: "http://www.w3.org/2000/svg",
3079
- "aria-hidden": true,
3080
- focusable: false
3081
- }, react.createElement("path", { d: "M3 6h18" }), react.createElement("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }), react.createElement("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" }), react.createElement("line", {
3082
- x1: 10,
3083
- y1: 11,
3084
- x2: 10,
3085
- y2: 17
3086
- }), react.createElement("line", {
3087
- x1: 14,
3088
- y1: 11,
3089
- x2: 14,
3090
- y2: 17
3091
- }));
3092
- }
3093
- /** Lucide `share`, path data verbatim; sends one theme out as an archive. */
3094
- function IconShare(props) {
3095
- const { size = 14 } = props;
3096
- return react.createElement("svg", {
3097
- width: size,
3098
- height: size,
3099
- viewBox: "0 0 24 24",
3100
- fill: "none",
3101
- stroke: "currentColor",
3102
- strokeWidth: 2,
3103
- strokeLinecap: "round",
3104
- strokeLinejoin: "round",
3105
- xmlns: "http://www.w3.org/2000/svg",
3106
- "aria-hidden": true,
3107
- focusable: false
3108
- }, react.createElement("path", { d: "M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" }), react.createElement("polyline", { points: "16 6 12 2 8 6" }), react.createElement("line", {
3109
- x1: 12,
3110
- y1: 2,
3111
- x2: 12,
3112
- y2: 15
3113
- }));
3114
- }
3115
- /** Lucide `download`, path data verbatim; takes one archive in. */
3116
- function IconDownload(props) {
3117
- const { size = 14 } = props;
3118
- return react.createElement("svg", {
3119
- width: size,
3120
- height: size,
3121
- viewBox: "0 0 24 24",
3122
- fill: "none",
3123
- stroke: "currentColor",
3124
- strokeWidth: 2,
3125
- strokeLinecap: "round",
3126
- strokeLinejoin: "round",
3127
- xmlns: "http://www.w3.org/2000/svg",
3128
- "aria-hidden": true,
3129
- focusable: false
3130
- }, react.createElement("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), react.createElement("polyline", { points: "7 10 12 15 17 10" }), react.createElement("line", {
3131
- x1: 12,
3132
- y1: 15,
3133
- x2: 12,
3134
- y2: 3
3135
- }));
3136
- }
3137
- /**
3138
3380
  * One collapsed saved-theme card: the name activates the theme, and the trailing
3139
3381
  * controls export it, open the editor or delete it. The card as a whole is a
3140
3382
  * click target too, but its controls own their clicks.
@@ -3142,22 +3384,29 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3142
3384
  function ThemeCard(props) {
3143
3385
  const { entry, active, disabled, t, onActivate, onExport, onEdit, onDelete } = props;
3144
3386
  /**
3145
- * One square icon action; `danger` is the tinted delete variant. The glyph is
3146
- * decorative, so the label travels as the accessible name instead of as text,
3147
- * and as `data-tip` for the hover tooltip. `leave` marks the button on the
3148
- * markup the outside-click guard watches (see the guard's own comment).
3387
+ * One square icon action. The glyph is decorative, so the label travels as
3388
+ * the accessible name and as the shell's own `Tooltip` above the button.
3389
+ * `danger` paints the error face; `leave` marks the button on the markup the
3390
+ * outside-click guard watches (see that guard) — it is not a visual state.
3149
3391
  */
3150
- const action = (label, icon, onClick, variant) => react.createElement("button", {
3151
- type: "button",
3152
- className: variant === "danger" ? "ct-list-btn ct-list-btn-danger ct-tip" : "ct-list-btn ct-tip",
3153
- "aria-label": label,
3154
- "data-tip": label,
3155
- ...variant === "leave" ? {
3156
- "data-ct-leave": "true",
3157
- "data-ct-entry": entry.id
3158
- } : {},
3159
- onClick
3160
- }, icon);
3392
+ const action = (label, icon, onClick, marks) => {
3393
+ const button = react.createElement("button", {
3394
+ type: "button",
3395
+ className: marks?.danger ? "ct-list-btn ct-list-btn-danger" : "ct-list-btn",
3396
+ "aria-label": label,
3397
+ ...marks?.leave ? {
3398
+ "data-ct-leave": "true",
3399
+ "data-ct-entry": entry.id
3400
+ } : {},
3401
+ onClick
3402
+ }, icon);
3403
+ return react.createElement(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
3404
+ label,
3405
+ side: "top",
3406
+ gap: 6,
3407
+ children: button
3408
+ });
3409
+ };
3161
3410
  return react.createElement("div", {
3162
3411
  className: "ct-list-row",
3163
3412
  "data-ct-leave": "true",
@@ -3174,7 +3423,7 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3174
3423
  }, entry.name)), react.createElement("div", {
3175
3424
  className: "ct-list-actions",
3176
3425
  onClick: (e) => e.stopPropagation()
3177
- }, active ? react.createElement("span", { className: "ct-list-badge" }, t("custom.inUse")) : null, action(t("custom.edit"), react.createElement(IconEdit, { size: 14 }), onEdit, "leave"), action(t("custom.export"), react.createElement(IconShare, { size: 14 }), onExport), action(t("custom.delete"), react.createElement(IconTrash, { size: 14 }), onDelete, "danger")));
3426
+ }, active ? react.createElement("span", { className: "ct-list-badge" }, t("custom.inUse")) : null, action(t("custom.edit"), react.createElement(_deepseek_ai_dsh_client_ui_primitives.IconEditOutlineRegular, { size: ICON_SIZE }), onEdit, { leave: true }), action(t("custom.export"), react.createElement(_deepseek_ai_dsh_client_ui_primitives.IconShareOutlineRegular, { size: ICON_SIZE }), onExport), action(t("custom.delete"), react.createElement(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineRegular, { size: ICON_SIZE }), onDelete, { danger: true })));
3178
3427
  }
3179
3428
  /**
3180
3429
  * Which appearance the colour dots edit. `system` resolves through the same
@@ -3190,7 +3439,7 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3190
3439
  }
3191
3440
  }
3192
3441
  function ThemePanel(props) {
3193
- const { theme, getSelection, setSelection, getCustom, setCustom, setCustomBase, resetCustom, saved, getHostToast, t, onRequestClose } = props;
3442
+ const { theme, getSelection, setSelection, getCustom, setCustom, setCustomBase, resetCustom, saved, getSidebarTransparent, setSidebarTransparent, t, onRequestClose } = props;
3194
3443
  let initScheme = "system";
3195
3444
  try {
3196
3445
  const snap = theme?.getTheme();
@@ -3209,6 +3458,7 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3209
3458
  });
3210
3459
  const [list, setList] = react.useState(() => saved.list());
3211
3460
  const [activeId, setActiveId] = react.useState(() => saved.activeId());
3461
+ const [sidebarTransparent, setSidebarTransparentState] = react.useState(() => getSidebarTransparent());
3212
3462
  const [pendingDelete, setPendingDelete] = react.useState(null);
3213
3463
  const [importing, setImporting] = react.useState(false);
3214
3464
  /** The hidden picker the import button opens. */
@@ -3240,6 +3490,72 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3240
3490
  text
3241
3491
  });
3242
3492
  }
3493
+ const [pickerDotKey, setPickerDotKey] = react.useState(null);
3494
+ const [pickerPos, setPickerPos] = react.useState(null);
3495
+ const pickerOpen = pickerDotKey !== null;
3496
+ const pickerOpenRef = react.useRef(false);
3497
+ pickerOpenRef.current = pickerOpen;
3498
+ /** The swatch the popover is anchored to; re-read on every reposition. */
3499
+ const pickerAnchorRef = react.useRef(null);
3500
+ /** The popover node itself; outside-pointerdown detection needs its bounds. */
3501
+ const pickerPopRef = react.useRef(null);
3502
+ /** Viewport coordinates that put the popover beside its anchor swatch. */
3503
+ function placePicker(anchor) {
3504
+ const WIDTH = 224;
3505
+ const HEIGHT = 240;
3506
+ const GAP = 8;
3507
+ const MARGIN = 8;
3508
+ const rect = anchor.getBoundingClientRect();
3509
+ const viewW = window.innerWidth;
3510
+ const viewH = window.innerHeight;
3511
+ return {
3512
+ left: Math.min(Math.max(MARGIN, rect.right - WIDTH), Math.max(MARGIN, viewW - WIDTH - MARGIN)),
3513
+ top: rect.bottom + GAP + HEIGHT <= viewH - MARGIN ? rect.bottom + GAP : Math.max(MARGIN, rect.top - GAP - HEIGHT)
3514
+ };
3515
+ }
3516
+ /** Toggle one seed's popover; `anchor` is the swatch that was clicked. */
3517
+ function togglePicker(key, anchor) {
3518
+ if (pickerDotKey === key) {
3519
+ closePicker();
3520
+ return;
3521
+ }
3522
+ pickerAnchorRef.current = anchor;
3523
+ setPickerDotKey(key);
3524
+ setPickerPos(placePicker(anchor));
3525
+ }
3526
+ function closePicker() {
3527
+ setPickerDotKey(null);
3528
+ setPickerPos(null);
3529
+ }
3530
+ react.useEffect(() => {
3531
+ if (!pickerOpen) return;
3532
+ function onPointerDown(e) {
3533
+ const target = e.target instanceof Element ? e.target : null;
3534
+ if (!target) return;
3535
+ if (pickerPopRef.current?.contains(target)) return;
3536
+ if (pickerAnchorRef.current?.contains(target)) return;
3537
+ closePicker();
3538
+ }
3539
+ document.addEventListener("pointerdown", onPointerDown, true);
3540
+ return () => document.removeEventListener("pointerdown", onPointerDown, true);
3541
+ }, [pickerOpen]);
3542
+ react.useEffect(() => {
3543
+ if (!pickerOpen) return;
3544
+ let raf = 0;
3545
+ const update = () => {
3546
+ const anchor = pickerAnchorRef.current;
3547
+ if (!anchor) return;
3548
+ cancelAnimationFrame(raf);
3549
+ raf = requestAnimationFrame(() => setPickerPos(placePicker(anchor)));
3550
+ };
3551
+ window.addEventListener("scroll", update, true);
3552
+ window.addEventListener("resize", update);
3553
+ return () => {
3554
+ window.removeEventListener("scroll", update, true);
3555
+ window.removeEventListener("resize", update);
3556
+ cancelAnimationFrame(raf);
3557
+ };
3558
+ }, [pickerOpen]);
3243
3559
  /** Re-read both the list and the active pointer after any list mutation. */
3244
3560
  function syncSaved() {
3245
3561
  setList(saved.list());
@@ -3361,6 +3677,12 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3361
3677
  }
3362
3678
  function onKeyDown(e) {
3363
3679
  if (e.key !== "Escape") return;
3680
+ if (pickerOpenRef.current) {
3681
+ e.preventDefault();
3682
+ e.stopPropagation();
3683
+ closePicker();
3684
+ return;
3685
+ }
3364
3686
  if (confirmCloseRef.current || pendingLeaveRef.current) {
3365
3687
  e.preventDefault();
3366
3688
  e.stopPropagation();
@@ -3378,6 +3700,7 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3378
3700
  const target = e.target instanceof Element ? e.target : null;
3379
3701
  if (!target) return;
3380
3702
  if (target.closest("[data-ct-editor]")) return;
3703
+ if (target.closest("[role=\"menu\"]")) return;
3381
3704
  const panel = settingsPanel();
3382
3705
  if (!!panel && !panel.contains(target) || isCloseButton(target)) {
3383
3706
  if (!onRequestClose) return;
@@ -3414,6 +3737,7 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3414
3737
  }, [onRequestClose]);
3415
3738
  /** Open a fresh, unsaved theme seeded from the currently selected preset. */
3416
3739
  function startAdd() {
3740
+ closePicker();
3417
3741
  const origin = beginEditing(null, nextThemeName(t("custom.defaultName"), list.map((e) => e.name)), "", getCustom().theme);
3418
3742
  setCustomBase(shownPreset);
3419
3743
  const draft = resetCustom();
@@ -3422,6 +3746,7 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3422
3746
  }
3423
3747
  /** Open an existing card for editing; it also becomes the previewed theme. */
3424
3748
  function startEdit(entry) {
3749
+ closePicker();
3425
3750
  const next = beginEditing(entry.id, entry.name, entry.name, entry.theme);
3426
3751
  setEditing(next);
3427
3752
  onLoadSaved(entry.id);
@@ -3429,6 +3754,7 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3429
3754
  /** Discard the open editor and put the previously applied theme back. */
3430
3755
  function cancelEdit() {
3431
3756
  if (!editing) return;
3757
+ closePicker();
3432
3758
  revertEditing(editing);
3433
3759
  setEditing(null);
3434
3760
  syncSaved();
@@ -3491,6 +3817,7 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3491
3817
  /** Commit the open editor: create a new entry, or update the one being edited. */
3492
3818
  async function saveEdit() {
3493
3819
  if (!editing) return;
3820
+ closePicker();
3494
3821
  const name = editing.name.trim() || t("custom.defaultName");
3495
3822
  const id = editing.id;
3496
3823
  savingRef.current = true;
@@ -3547,6 +3874,7 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3547
3874
  setCustomState(getCustom().theme);
3548
3875
  return;
3549
3876
  }
3877
+ closePicker();
3550
3878
  if (editing) revertEditing(editing);
3551
3879
  setEditing(null);
3552
3880
  setSelectionState(shownPreset);
@@ -3590,10 +3918,21 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3590
3918
  onSelect: (v) => pickPreset(v),
3591
3919
  disabled: customOn
3592
3920
  })),
3921
+ react.createElement("div", {
3922
+ className: "ct-row",
3923
+ key: "sidebar-transparency"
3924
+ }, react.createElement("div", { className: "ct-row-main" }, react.createElement("div", { className: "ct-row-title" }, t("sidebar.title")), react.createElement("div", { className: "ct-row-desc" }, t("sidebar.desc"))), react.createElement(_deepseek_ai_dsh_client_ui_primitives.Switch, {
3925
+ checked: sidebarTransparent,
3926
+ label: t("sidebar.title"),
3927
+ onChange: (on) => {
3928
+ setSidebarTransparentState(on);
3929
+ setSidebarTransparent(on);
3930
+ }
3931
+ })),
3593
3932
  react.createElement("div", {
3594
3933
  className: "ct-row ct-row-flush",
3595
3934
  key: "custom-toggle"
3596
- }, react.createElement("div", { className: "ct-row-main" }, react.createElement("div", { className: "ct-row-title" }, t("custom.title")), react.createElement("div", { className: "ct-row-desc" }, t("custom.toggle.desc"))), react.createElement(Switch, {
3935
+ }, react.createElement("div", { className: "ct-row-main" }, react.createElement("div", { className: "ct-row-title" }, t("custom.title")), react.createElement("div", { className: "ct-row-desc" }, t("custom.toggle.desc"))), react.createElement(_deepseek_ai_dsh_client_ui_primitives.Switch, {
3597
3936
  checked: customOn,
3598
3937
  label: t("custom.title"),
3599
3938
  onChange: toggleCustom
@@ -3604,18 +3943,21 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3604
3943
  const dotRow = (key, label, dots) => react.createElement("div", {
3605
3944
  className: "ct-seed-row",
3606
3945
  key
3607
- }, react.createElement("div", { className: "ct-seed-label" }, label), react.createElement("div", { className: "ct-seed-dots" }, ...dots.map((d) => react.createElement("label", {
3946
+ }, react.createElement("div", { className: "ct-seed-label" }, label), react.createElement("div", { className: "ct-seed-dots" }, ...dots.map((d) => react.createElement(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
3608
3947
  key: d.key,
3609
- className: "ct-seed-dot ct-tip",
3610
- style: { background: d.value },
3611
- "data-tip": d.name
3612
- }, react.createElement("input", {
3613
- className: "ct-seed-input",
3614
- type: "color",
3615
- value: d.value,
3616
- "aria-label": `${d.name} — ${d.hint}`,
3617
- onChange: (e) => d.onChange(e.target.value.toUpperCase())
3618
- })))));
3948
+ label: d.name,
3949
+ side: "top",
3950
+ gap: 6,
3951
+ children: react.createElement("button", {
3952
+ type: "button",
3953
+ className: "ct-seed-dot",
3954
+ style: { background: d.value },
3955
+ "aria-haspopup": "dialog",
3956
+ "aria-expanded": pickerDotKey === d.key,
3957
+ "aria-label": `${d.name} — ${d.hint}`,
3958
+ onClick: (e) => togglePicker(d.key, e.currentTarget)
3959
+ })
3960
+ }))));
3619
3961
  const pair = (key, title, desc) => ({
3620
3962
  key,
3621
3963
  name: t(title),
@@ -3672,21 +4014,24 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3672
4014
  })
3673
4015
  }));
3674
4016
  /** The light/dark segmented control above the dots. */
3675
- const modeButton = (id, label) => react.createElement("button", {
3676
- key: id,
3677
- type: "button",
3678
- className: seedMode === id ? "ct-mode-btn ct-mode-btn-on" : "ct-mode-btn",
3679
- "aria-pressed": seedMode === id,
3680
- onClick: () => {
3681
- setSeedMode(id);
3682
- previewAppearance(id);
3683
- }
3684
- }, label);
3685
- const modeToggle = react.createElement("div", {
3686
- className: "ct-mode",
3687
- role: "group",
3688
- "aria-label": t("custom.mode.label")
3689
- }, modeButton("light", t("custom.seed.light")), modeButton("dark", t("custom.seed.dark")));
4017
+ const modeToggle = react.createElement(_deepseek_ai_dsh_client_ui_primitives.SegmentedControl, {
4018
+ id: SEED_MODE_CONTROL_ID,
4019
+ value: seedMode,
4020
+ options: [{
4021
+ value: "light",
4022
+ label: t("custom.seed.light")
4023
+ }, {
4024
+ value: "dark",
4025
+ label: t("custom.seed.dark")
4026
+ }],
4027
+ onChange: (next) => {
4028
+ const mode = next;
4029
+ setSeedMode(mode);
4030
+ previewAppearance(mode);
4031
+ },
4032
+ label: t("custom.mode.label"),
4033
+ className: "ct-mode"
4034
+ });
3690
4035
  const editorName = react.createElement("input", {
3691
4036
  className: "ct-editor-name",
3692
4037
  type: "text",
@@ -3703,20 +4048,40 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3703
4048
  else if (e.key === "Escape") cancelEdit();
3704
4049
  }
3705
4050
  });
3706
- const editorActions = react.createElement("div", { className: "ct-editor-actions" }, react.createElement("button", {
3707
- type: "button",
3708
- className: "ct-btn",
4051
+ const editorActions = react.createElement("div", { className: "ct-editor-actions" }, react.createElement(_deepseek_ai_dsh_client_ui_primitives.Button, {
4052
+ variant: "outline",
3709
4053
  onClick: cancelEdit
3710
- }, t("custom.cancel")), react.createElement("button", {
3711
- type: "button",
3712
- className: "ct-btn ct-btn-primary",
4054
+ }, t("custom.cancel")), react.createElement(_deepseek_ai_dsh_client_ui_primitives.Button, {
4055
+ variant: "primary",
3713
4056
  onClick: () => void saveEdit()
3714
4057
  }, t("custom.save")));
4058
+ const pickerDot = pickerDotKey ? [...seedDots, ...shikiDots].find((d) => d.key === pickerDotKey) ?? null : null;
4059
+ const pickerPop = pickerDot && pickerPos ? react.createElement("div", {
4060
+ key: "color-picker",
4061
+ className: "ct-color-pop",
4062
+ ref: pickerPopRef,
4063
+ style: {
4064
+ left: pickerPos.left,
4065
+ top: pickerPos.top
4066
+ }
4067
+ }, react.createElement(Z, {
4068
+ color: pickerDot.value,
4069
+ onChange: (v) => pickerDot.onChange(v.toUpperCase())
4070
+ }), react.createElement(Oe, {
4071
+ className: "ct-hex-input",
4072
+ color: pickerDot.value,
4073
+ "aria-label": pickerDot.name,
4074
+ onChange: (v) => pickerDot.onChange(v.toUpperCase())
4075
+ })) : null;
3715
4076
  const editorCard = (key) => react.createElement("div", {
3716
4077
  className: "ct-editor",
3717
4078
  key,
3718
4079
  "data-ct-editor": "true"
3719
- }, react.createElement("div", { className: "ct-editor-head" }, editorName, modeToggle), dotRow("seeds", t("custom.group.seeds"), seedDots), dotRow("shiki", t("custom.group.shiki"), shikiDots), editorActions);
4080
+ }, react.createElement("div", { className: "ct-editor-head" }, editorName, modeToggle), react.createElement("div", {
4081
+ role: "tabpanel",
4082
+ id: `${SEED_MODE_CONTROL_ID}-${seedMode}-panel`,
4083
+ "aria-labelledby": `${SEED_MODE_CONTROL_ID}-${seedMode}`
4084
+ }, dotRow("seeds", t("custom.group.seeds"), seedDots), dotRow("shiki", t("custom.group.shiki"), shikiDots)), editorActions, pickerPop);
3720
4085
  const cards = list.map((entry) => editing && editing.id === entry.id ? editorCard(entry.id) : react.createElement(ThemeCard, {
3721
4086
  key: entry.id,
3722
4087
  entry,
@@ -3741,13 +4106,13 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3741
4106
  className: "ct-add-btn",
3742
4107
  disabled: importing,
3743
4108
  onClick: startImport
3744
- }, react.createElement(IconDownload, null), t("custom.import")), react.createElement("button", {
4109
+ }, react.createElement(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutlineRegular, { size: ICON_SIZE }), t("custom.import")), react.createElement("button", {
3745
4110
  key: "add",
3746
4111
  type: "button",
3747
4112
  className: "ct-add-btn",
3748
4113
  disabled: editing !== null,
3749
4114
  onClick: startAdd
3750
- }, react.createElement(IconPlus, null), t("custom.add")), react.createElement("input", {
4115
+ }, react.createElement(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: ICON_SIZE }), t("custom.add")), react.createElement("input", {
3751
4116
  key: "import-input",
3752
4117
  ref: importInputRef,
3753
4118
  type: "file",
@@ -3762,6 +4127,7 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3762
4127
  description: t("custom.delete.desc").replace("{0}", pendingDelete.name),
3763
4128
  confirmLabel: t("custom.delete"),
3764
4129
  cancelLabel: t("custom.cancel"),
4130
+ danger: true,
3765
4131
  onConfirm: onConfirmDelete,
3766
4132
  onCancel: () => setPendingDelete(null)
3767
4133
  }));
@@ -3794,20 +4160,12 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3794
4160
  },
3795
4161
  onCancel: () => setPendingLeave(null)
3796
4162
  }));
3797
- if (toast) {
3798
- const onDone = () => setToast((cur) => cur?.seq === toast.seq ? null : cur);
3799
- const HostToast = getHostToast();
3800
- children.push(HostToast ? react.createElement(HostToast, {
3801
- key: `toast-${String(toast.seq)}`,
3802
- text: toast.text,
3803
- icon: react.createElement(ToastBadge),
3804
- onDone
3805
- }) : react.createElement(Toast, {
3806
- key: `toast-${String(toast.seq)}`,
3807
- text: toast.text,
3808
- onDone
3809
- }));
3810
- }
4163
+ if (toast) children.push(react.createElement(_deepseek_ai_dsh_client_ui_primitives.Toast, {
4164
+ key: `toast-${String(toast.seq)}`,
4165
+ text: toast.text,
4166
+ tone: "success",
4167
+ onDone: () => setToast((cur) => cur?.seq === toast.seq ? null : cur)
4168
+ }));
3811
4169
  return react.createElement("div", { style: {
3812
4170
  display: "flex",
3813
4171
  flexDirection: "column",
@@ -3830,6 +4188,8 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3830
4188
  "presets.title": "预设主题",
3831
4189
  "presets.desc": "一键套用主流配色方案",
3832
4190
  "presets.disabled": "自定义主题已启用,关闭后才能选择预设",
4191
+ "sidebar.title": "侧边栏透明",
4192
+ "sidebar.desc": "侧边栏面板透明效果,仅 Mac 端生效",
3833
4193
  "scheme.light": "浅色",
3834
4194
  "scheme.dark": "深色",
3835
4195
  "scheme.system": "跟随系统",
@@ -3903,6 +4263,8 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3903
4263
  "presets.title": "Preset themes",
3904
4264
  "presets.desc": "Apply popular color schemes in one click",
3905
4265
  "presets.disabled": "Custom theme is on — turn it off to choose a preset",
4266
+ "sidebar.title": "Transparent sidebar",
4267
+ "sidebar.desc": "Sidebar panel transparency effect, macOS only",
3906
4268
  "scheme.light": "Light",
3907
4269
  "scheme.dark": "Dark",
3908
4270
  "scheme.system": "System",
@@ -3978,19 +4340,6 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
3978
4340
  ".ct-select-label{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left;}",
3979
4341
  ".ct-select-chevron{flex:none;color:var(--dsw-alias-label-tertiary);display:inline-flex;transition:transform 120ms ease;}",
3980
4342
  ".ct-select[aria-expanded=\"true\"] .ct-select-chevron{transform:rotate(180deg);}",
3981
- ".ct-menu-list{box-sizing:border-box;padding:4px;display:flex;flex-direction:column;gap:0;border:1px solid var(--dsw-alias-border-inverted);border-radius:12px;background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv3);min-width:218px;max-width:360px;max-height:min(320px,50vh);overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:var(--dsw-alias-border-l2) transparent;scrollbar-gutter:stable;}",
3982
- ".ct-menu-list:not(:hover){scrollbar-color:transparent transparent;}",
3983
- ".ct-menu-list::-webkit-scrollbar{width:8px;height:8px;}",
3984
- ".ct-menu-list::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l2);border-radius:999px;border:2px solid var(--dsw-specific-menu);background-clip:content-box;transition:background 150ms ease, border-color 150ms ease;}",
3985
- ".ct-menu-list::-webkit-scrollbar-thumb:hover{background:var(--dsw-alias-label-tertiary);border:2px solid var(--dsw-specific-menu);background-clip:content-box;}",
3986
- ".ct-menu-list::-webkit-scrollbar-thumb:active{background:var(--dsw-alias-label-secondary, var(--dsw-alias-label-tertiary));border:2px solid var(--dsw-specific-menu);background-clip:content-box;}",
3987
- ".ct-menu-list:not(:hover)::-webkit-scrollbar-thumb{background:transparent;border-color:transparent;}",
3988
- ".ct-menu-list::-webkit-scrollbar-track{background:transparent;margin:4px 0;}",
3989
- ".ct-menu-list::-webkit-scrollbar-corner{background:transparent;}",
3990
- ".ct-menu-item{display:flex;align-items:center;gap:8px;width:100%;min-height:40px;padding:8px 10px;border:none;border-radius:10px;background:transparent;cursor:pointer;font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary);text-align:left;font-family:inherit;}",
3991
- ".ct-menu-item:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);}",
3992
- ".ct-menu-check{flex:none;color:var(--dsw-alias-label-primary);display:inline-flex;margin-left:auto;}",
3993
- ".ct-menu-item-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
3994
4343
  ".ct-row{border-bottom:1px solid var(--dsw-alias-border-l2);padding:16px 2px;display:flex;align-items:center;gap:16px;}",
3995
4344
  ".ct-row:last-child{border-bottom:none;}",
3996
4345
  ".ct-row-flush{border-bottom:none;}",
@@ -4000,13 +4349,12 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
4000
4349
  ".ct-seed-row{display:flex;align-items:center;gap:16px;padding:12px 0;}",
4001
4350
  ".ct-seed-label{flex:0 0 auto;min-width:96px;padding-left:3px;font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary);}",
4002
4351
  ".ct-seed-dots{flex:1 1 auto;display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:10px;}",
4003
- ".ct-seed-dot{position:relative;box-sizing:border-box;display:inline-block;flex:none;width:26px;height:26px;border:1px solid var(--dsw-alias-border-l2);border-radius:50%;cursor:pointer;}",
4004
- ".ct-seed-input{position:absolute;inset:0;box-sizing:border-box;width:100%;height:100%;padding:0;border:none;background:transparent;opacity:0;cursor:pointer;}",
4005
- ".ct-seed-dot:focus-within{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3);}",
4006
- ".ct-tip::after{content:attr(data-tip);position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);z-index:100;box-sizing:border-box;width:max-content;max-width:220px;padding:3px 7px;border-radius:8px;background:var(--dsw-alias-tooltip-bg);color:var(--dsw-static-neutral-bluish-00);font-size:13px;font-weight:400;line-height:20px;white-space:pre-line;overflow-wrap:break-word;text-align:left;pointer-events:none;opacity:0;visibility:hidden;transition:opacity 120ms ease;}",
4007
- ".ct-seed-dot:hover::after,.ct-seed-dot:focus-within::after{opacity:1;visibility:visible;}",
4008
- ".ct-list-btn:hover::after,.ct-list-btn:focus-visible::after{opacity:1;visibility:visible;}",
4009
- "@media (prefers-reduced-motion: reduce){.ct-tip::after{transition:none;}}",
4352
+ ".ct-seed-dot{position:relative;box-sizing:border-box;display:inline-block;flex:none;width:26px;height:26px;padding:0;border:1px solid var(--dsw-alias-border-l2);border-radius:50%;cursor:pointer;font:inherit;}",
4353
+ ".ct-seed-dot:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3);}",
4354
+ ".ct-color-pop{position:fixed;z-index:1200;box-sizing:border-box;width:224px;padding:12px;border:1px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-bg-layer-2);box-shadow:var(--dsw-shadow-lv3);display:flex;flex-direction:column;gap:10px;}",
4355
+ ".ct-color-pop .react-colorful{width:198px;height:170px;}",
4356
+ ".ct-color-pop .ct-hex-input{box-sizing:border-box;width:100%;height:34px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:20px;text-align:center;outline:none;}",
4357
+ ".ct-color-pop .ct-hex-input:focus{border-color:var(--dsw-alias-border-l3);}",
4010
4358
  ".ct-list{margin-top:4px;display:flex;flex-direction:column;gap:8px;}",
4011
4359
  ".ct-list-row{display:flex;align-items:center;gap:10px;padding:12px 14px;border:.5px solid var(--dsw-alias-border-l4);border-radius:16px;background:transparent;cursor:pointer;transition:border-color .16s ease,background .16s ease;}",
4012
4360
  ".ct-list-row:hover{background:var(--dsw-alias-interactive-bg-hover);}",
@@ -4016,7 +4364,7 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
4016
4364
  ".ct-list-actions{display:inline-flex;align-items:center;gap:4px;flex:none;margin-left:auto;}",
4017
4365
  ".ct-list-badge{display:inline-flex;align-items:center;height:28px;padding:0 10px;border-radius:14px;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary));font-size:12px;line-height:18px;white-space:nowrap;}",
4018
4366
  ".ct-list-badge + .ct-list-btn{margin-left:8px;}",
4019
- ".ct-list-btn{position:relative;box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;height:28px;width:28px;padding:0;border:none;border-radius:14px;background:transparent;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;line-height:0;}",
4367
+ ".ct-list-btn{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;height:28px;width:28px;padding:0;border:none;border-radius:14px;background:transparent;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;line-height:0;}",
4020
4368
  ".ct-list-btn:hover{background:var(--dsw-alias-interactive-bg-hover);}",
4021
4369
  ".ct-list-btn:active{background:var(--dsw-alias-interactive-bg-active);}",
4022
4370
  ".ct-list-btn:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3);}",
@@ -4025,19 +4373,15 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
4025
4373
  ".ct-list-btn-danger{color:var(--dsw-alias-state-error-primary);}",
4026
4374
  ".ct-list-btn-danger:hover{background:var(--dsw-alias-interactive-bg-hover-danger);}",
4027
4375
  ".ct-list-btn-danger:active{background:var(--dsw-alias-interactive-bg-hover-danger);}",
4028
- ".ct-editor{padding:16px;border:.5px solid var(--dsw-alias-border-l4);border-radius:16px;background:transparent;display:flex;flex-direction:column;gap:0;}",
4376
+ ".ct-editor{padding:16px;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-lg);background:transparent;display:flex;flex-direction:column;gap:0;}",
4029
4377
  ".ct-editor-head{display:flex;align-items:center;gap:10px;padding-bottom:12px;}",
4030
4378
  ".ct-editor-name{flex:1 1 auto;min-width:0;box-sizing:border-box;height:36px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:14px;line-height:22px;}",
4031
4379
  ".ct-editor-name::placeholder{color:var(--dsw-alias-label-tertiary);}",
4032
4380
  ".ct-editor-name:hover{border-color:var(--dsw-alias-border-l3);}",
4033
4381
  ".ct-editor-name:focus{outline:none;border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-bg-base);}",
4034
- ".ct-mode{box-sizing:border-box;display:inline-flex;flex:none;height:36px;padding:2px;border-radius:10px;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-2));}",
4035
- ".ct-mode-btn{box-sizing:border-box;height:32px;padding:0 10px;border:none;border-radius:8px;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;font:inherit;font-size:12px;line-height:18px;}",
4036
- ".ct-mode-btn:hover{color:var(--dsw-alias-label-primary);}",
4037
- ".ct-mode-btn-on{background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);}",
4038
- ".ct-mode-btn:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3);}",
4382
+ ".ct-mode{flex:none;}",
4039
4383
  ".ct-editor-actions{display:flex;justify-content:flex-end;gap:12px;padding-top:12px;}",
4040
- ".ct-add-btn{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:6px;width:100%;height:44px;margin-top:8px;padding:0 14px;border:1px dashed var(--dsw-alias-border-l3);border-radius:16px;background:transparent;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:14px;line-height:22px;}",
4384
+ ".ct-add-btn{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:6px;width:100%;height:44px;margin-top:8px;padding:0 14px;border:1px dashed var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-lg);background:transparent;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:14px;line-height:22px;}",
4041
4385
  ".ct-add-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);}",
4042
4386
  ".ct-add-btn:disabled{opacity:.4;cursor:default;}",
4043
4387
  ".ct-add-btn:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3);}",
@@ -4045,28 +4389,13 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
4045
4389
  ".ct-add-row .ct-add-btn{flex:1 1 0;width:auto;margin-top:0;min-width:0;}",
4046
4390
  ".ct-modal-root{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;padding:24px;}",
4047
4391
  ".ct-modal-mask{position:absolute;inset:0;background:var(--dsw-alias-bg-mask-1,rgba(0,0,0,.24));backdrop-filter:var(--dsw-mask-blur,blur(2px));}",
4048
- ".ct-modal-card{position:relative;z-index:1;display:flex;flex-direction:column;gap:12px;width:min(380px,100%);padding:24px;border-radius:24px;background:var(--dsw-alias-bg-layer-2);box-shadow:var(--dsw-elevation-prominent);}",
4392
+ ".ct-modal-card{position:relative;z-index:1;display:flex;flex-direction:column;gap:12px;width:min(380px,100%);padding:24px;border-radius:var(--dsw-radius-panel);background:var(--dsw-alias-bg-layer-2);box-shadow:var(--dsw-elevation-prominent);}",
4049
4393
  ".ct-modal-title{font-size:16px;line-height:24px;font-weight:600;color:var(--dsw-alias-label-primary);}",
4050
4394
  ".ct-modal-desc{font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary);}",
4051
4395
  ".ct-modal-footer{display:flex;justify-content:flex-end;gap:12px;margin-top:12px;}",
4052
- ".ct-switch{position:relative;flex:none;width:40px;height:24px;padding:0;border:none;border-radius:12px;background:var(--dsw-alias-border-l2);cursor:pointer;transition:background 150ms ease;}",
4053
- ".ct-switch-on{background:var(--dsw-static-deepseek-500,rgb(65,118,230));}",
4054
- ".ct-switch-knob{position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:transform 150ms ease;}",
4055
- ".ct-switch-on .ct-switch-knob{transform:translateX(16px);}",
4056
- ".ct-switch:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3);}",
4057
- ".ct-btn{box-sizing:border-box;display:inline-flex;align-items:center;height:34px;padding:0 16px;border:1px solid var(--dsw-alias-border-l2);border-radius:17px;background:transparent;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:13px;line-height:20px;}",
4058
- ".ct-btn:hover{background:var(--dsw-alias-interactive-bg-hover);}",
4059
- ".ct-btn:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3);}",
4060
- ".ct-btn-primary{background:var(--dsw-static-deepseek-500,rgb(65,118,230));border-color:transparent;color:#fff;}",
4061
- ".ct-btn-primary:hover{background:var(--dsw-static-deepseek-450,var(--dsw-static-deepseek-500,rgb(86,134,254)));}",
4062
- ".ct-btn-danger{background:var(--dsw-static-red-500,rgb(239,68,68));border-color:transparent;color:#fff;}",
4063
- ".ct-btn-danger:hover{background:var(--dsw-static-red-600,var(--dsw-static-red-500,rgb(236,19,19)));}",
4064
- ".ct-toast{position:fixed;top:40px;left:50%;z-index:1100;pointer-events:none;display:flex;align-items:center;gap:10px;width:max-content;max-width:min(640px,calc(100vw - 48px));padding:12px 16px;border-radius:14px;background:var(--dsw-alias-button-contrast-fill);color:var(--dsw-alias-label-primary-inverted);font-size:14px;line-height:22px;box-shadow:var(--dsw-shadow-lv3);transform:translateX(-50%);animation:ct-toast-in 160ms ease-out,ct-toast-fade 1s ease 3s forwards;}",
4065
- ".ct-toast-icon{display:grid;place-items:center;flex:none;width:18px;height:18px;border:1.5px solid var(--dsw-alias-state-success-primary);border-radius:50%;corner-shape:round;color:var(--dsw-alias-state-success-primary);}",
4066
- ".ct-toast-text{min-width:0;}",
4067
- "@keyframes ct-toast-in{from{opacity:0;transform:translate(-50%,-6px);}to{opacity:1;transform:translate(-50%,0);}}",
4068
- "@keyframes ct-toast-fade{to{opacity:0;}}",
4069
- "@media (prefers-reduced-motion: reduce){.ct-toast{animation:ct-toast-fade 1s ease 3s forwards;}}"
4396
+ ".ct-action-cancel{min-width:72px}",
4397
+ ".ct-action-confirm{min-width:136px}",
4398
+ "button.ct-danger{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 30%, transparent);--dsw-alias-interactive-bg-hover:color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent);}"
4070
4399
  ].join("\n");
4071
4400
  function isValidSelection(v) {
4072
4401
  return !!v && (v === CUSTOM_SELECTION || NOOP_PRESET_IDS.has(v) || v in PRESETS);
@@ -4122,6 +4451,25 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
4122
4451
  else localStorage.setItem(key, value);
4123
4452
  } catch {}
4124
4453
  }
4454
+ const SIDEBAR_OPAQUE_CSS = "html[data-platform=\"darwin\"] [class*=\"sidebarCol\"]:not([class*=\"sidebarCollapsed\"]){background:var(--dsw-specific-sidebar-fill) !important;}";
4455
+ let sidebarOpaqueDisposer = null;
4456
+ /**
4457
+ * Whether the sidebar keeps DSH's translucency. Only an explicit '1' turns it
4458
+ * on: an absent key means opaque, which is the state a fresh install starts in.
4459
+ */
4460
+ function sidebarTransparent() {
4461
+ return readRaw(SIDEBAR_TRANSPARENT_STORAGE_KEY) === "1";
4462
+ }
4463
+ /** Paint the sidebar column opaque (on), or hand it back to DSH (off). */
4464
+ function applySidebarOpaque(opaque) {
4465
+ if (opaque) {
4466
+ if (!sidebarOpaqueDisposer) sidebarOpaqueDisposer = injector.insert(SIDEBAR_OPAQUE_CSS);
4467
+ } else if (sidebarOpaqueDisposer) sidebarOpaqueDisposer = release(sidebarOpaqueDisposer);
4468
+ }
4469
+ function setSidebarTransparent(on) {
4470
+ writeRaw(SIDEBAR_TRANSPARENT_STORAGE_KEY, on ? "1" : "0");
4471
+ applySidebarOpaque(!on);
4472
+ }
4125
4473
  function getSelection() {
4126
4474
  for (const key of [THEME_STORAGE_KEY, THEME_STORAGE_KEY_LEGACY]) {
4127
4475
  const cur = readRaw(key);
@@ -4189,8 +4537,8 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
4189
4537
  /**
4190
4538
  * Coalesce a burst of palette applications into one.
4191
4539
  *
4192
- * A native colour input fires `change` on every pointer move while its slider
4193
- * is dragged. Each application rebuilds both ramps, rewrites every token on
4540
+ * A colour picker fires continuously while its slider is dragged. Each
4541
+ * application rebuilds both ramps, rewrites every token on
4194
4542
  * `body` and forces a style flush in the shell's presenter, so applying once
4195
4543
  * per event is what makes the colour editor stutter. The first change lands
4196
4544
  * immediately and the rest are throttled to one per interval, newest request
@@ -4345,13 +4693,7 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
4345
4693
  if (env && isPresetId(env.base)) lastPreset = env.base;
4346
4694
  }
4347
4695
  apply(initialSelection);
4348
- let sharedToast = null;
4349
- const modules = ctx.get("modules");
4350
- try {
4351
- modules?.import?.("@deepseek-ai/dsh-client-ui-primitives").then((mod) => {
4352
- if (typeof mod?.Toast === "function") sharedToast = mod.Toast;
4353
- }).catch(() => {});
4354
- } catch {}
4696
+ applySidebarOpaque(!sidebarTransparent());
4355
4697
  ctx.effect(() => () => {
4356
4698
  disposed = true;
4357
4699
  cancelApply();
@@ -4359,6 +4701,7 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
4359
4701
  baselineDisposer = release(baselineDisposer);
4360
4702
  overrideDispose = release(overrideDispose);
4361
4703
  fallbackDispose = release(fallbackDispose);
4704
+ sidebarOpaqueDisposer = release(sidebarOpaqueDisposer);
4362
4705
  injector.disposeAll();
4363
4706
  });
4364
4707
  slots.inject("settings.section", () => {
@@ -4379,7 +4722,8 @@ html[data-ds-dark-theme], body[data-ds-dark-theme] {
4379
4722
  setCustomBase,
4380
4723
  resetCustom,
4381
4724
  saved,
4382
- getHostToast: () => sharedToast,
4725
+ getSidebarTransparent: sidebarTransparent,
4726
+ setSidebarTransparent,
4383
4727
  onRequestClose: typeof props?.close === "function" ? props.close : null
4384
4728
  }));
4385
4729
  });