@linxin666/dsh-client-ui-skin-center 0.3.19 → 0.3.21

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/index.js CHANGED
@@ -717,6 +717,11 @@ const REVIEWED_SKIN_HOOKS = {
717
717
  manifestSha256: "b22cc82145e1f90f4257af1411724e34a99513761290980fb5f8d25727809808",
718
718
  hooksSha256: "21ac2ad4d4423acf31e3391bfba18ce2d9eec7b192f7a0ba47e8a0c843ff15a5"
719
719
  },
720
+ "blueprint": {
721
+ entry: "hooks.mjs",
722
+ manifestSha256: "e36d9d53aae73c4693e36fc2e130bca2996cda6dcd8f80917627897774232a3e",
723
+ hooksSha256: "4f6c7db598e72469920d1dbafd5c20cec39f435f225220ffc860636523ed70bb"
724
+ },
720
725
  "cyber-night": {
721
726
  entry: "hooks.mjs",
722
727
  manifestSha256: "38de22962a80602c22910324e7c5fec171342363760972b7421debeb628d8508",
@@ -744,8 +749,8 @@ const REVIEWED_SKIN_HOOKS = {
744
749
  },
745
750
  "maid-atelier": {
746
751
  entry: "hooks.mjs",
747
- manifestSha256: "f36e854f52f24939dfe6318ed6b1ff6fe33d0564e63255a22aaca459af1a84d9",
748
- hooksSha256: "8a50922c970337ab07b0843e61b9a771f3a57baf54d18a24e906f13dbb39b4ff"
752
+ manifestSha256: "7596a704bce65006381d27417d4c12bb09d7e5ede038a6f486cfa58e62314aa5",
753
+ hooksSha256: "c4668f9cee8192fe3c25e6a01b779bc9ec9ab1980061106e749551a7bafa4153"
749
754
  },
750
755
  "matrix": {
751
756
  entry: "hooks.mjs",
@@ -805,7 +810,7 @@ const REVIEWED_SKIN_HOOKS = {
805
810
  "xp": {
806
811
  entry: "hooks.mjs",
807
812
  manifestSha256: "8bceb95c45b400b67ceb80a7f063b6c2086c0e9f9907a7b72a592d33017623eb",
808
- hooksSha256: "823f25fd5f969feb8e51d8ff1577c1cbecbad847b2abac6e5020fc9ef543663f"
813
+ hooksSha256: "1574d30f271f481a681feddc0608b04b0aec684e323fec0b00481f08450e4eb3"
809
814
  }
810
815
  };
811
816
  //#endregion
@@ -5484,6 +5489,7 @@ function buildSceneManifestVia(access, token, projectOverride) {
5484
5489
  const width = typeof projW === "number" && Number.isFinite(projW) && projW > 0 ? Math.floor(projW) : 3840;
5485
5490
  const height = typeof projH === "number" && Number.isFinite(projH) && projH > 0 ? Math.floor(projH) : 2160;
5486
5491
  const resourceBase = "/api/skin-center/we/scene-resource/" + token + "/";
5492
+ const resourceUrl = (pkgPath) => resourceBase + pkgPath.split("/").map(encodeURIComponent).join("/");
5487
5493
  const manifest = {
5488
5494
  width,
5489
5495
  height,
@@ -5724,7 +5730,7 @@ function buildSceneManifestVia(access, token, projectOverride) {
5724
5730
  uv2B64: m.uv2 ? Buffer$1.from(m.uv2.buffer, m.uv2.byteOffset, m.uv2.byteLength).toString("base64") : void 0,
5725
5731
  indicesB64: Buffer$1.from(m.indices.buffer, m.indices.byteOffset, m.indices.byteLength).toString("base64"),
5726
5732
  idx32: m.indices instanceof Uint32Array || void 0,
5727
- texUrl: subTex ? resourceBase + subTex : void 0,
5733
+ texUrl: subTex ? resourceUrl(subTex) : void 0,
5728
5734
  repeatBase: m.uv.some((value) => value < 0 || value > 1) || void 0,
5729
5735
  materialPath: m.materialPath,
5730
5736
  shader,
@@ -5733,8 +5739,8 @@ function buildSceneManifestVia(access, token, projectOverride) {
5733
5739
  noDepthWrite,
5734
5740
  tint,
5735
5741
  tint2,
5736
- texUrl2: texPath2 ? resourceBase + texPath2 : void 0,
5737
- lightmapUrl: lightmapPath ? resourceBase + lightmapPath : void 0,
5742
+ texUrl2: texPath2 ? resourceUrl(texPath2) : void 0,
5743
+ lightmapUrl: lightmapPath ? resourceUrl(lightmapPath) : void 0,
5738
5744
  translucent,
5739
5745
  gradFade,
5740
5746
  userColors,
@@ -5796,7 +5802,7 @@ function buildSceneManifestVia(access, token, projectOverride) {
5796
5802
  manifest.bgLayers.push({
5797
5803
  name: typeof obj.name === "string" ? obj.name : "fullscreen",
5798
5804
  shader: typeof pass0.shader === "string" ? pass0.shader : void 0,
5799
- texUrl: texPath ? resourceBase + texPath : void 0,
5805
+ texUrl: texPath ? resourceUrl(texPath) : void 0,
5800
5806
  userColors: Object.keys(userColors).length > 0 ? userColors : void 0,
5801
5807
  userNums: Object.keys(userNums).length > 0 ? userNums : void 0
5802
5808
  });
@@ -5819,7 +5825,7 @@ function buildSceneManifestVia(access, token, projectOverride) {
5819
5825
  manifest.sprites = manifest.sprites ?? [];
5820
5826
  manifest.sprites.push({
5821
5827
  name: typeof obj.name === "string" ? obj.name : "sprite",
5822
- texUrl: texPath ? resourceBase + texPath : void 0,
5828
+ texUrl: texPath ? resourceUrl(texPath) : void 0,
5823
5829
  origin: parseVec3(obj.origin, [
5824
5830
  0,
5825
5831
  0,
@@ -5869,7 +5875,7 @@ function buildSceneManifestVia(access, token, projectOverride) {
5869
5875
  manifest.particles3d = manifest.particles3d ?? [];
5870
5876
  manifest.particles3d.push({
5871
5877
  name: typeof obj.name === "string" ? obj.name : "particles",
5872
- texUrl: texPath ? resourceBase + texPath : void 0,
5878
+ texUrl: texPath ? resourceUrl(texPath) : void 0,
5873
5879
  origin: [
5874
5880
  objOrigin[0] + emitterOrigin[0],
5875
5881
  objOrigin[1] + emitterOrigin[1],
@@ -5914,9 +5920,9 @@ function buildSceneManifestVia(access, token, projectOverride) {
5914
5920
  if (nameLower.includes("fireflies") || nameLower.includes("motes") || nameLower.includes("dust")) manifest.hasFireflies = true;
5915
5921
  }
5916
5922
  const meteorTexPath = allTex.find((p) => p.toLowerCase().includes("shootingstar") || p.toLowerCase().includes("meteor"));
5917
- if (meteorTexPath) manifest.meteorTex = resourceBase + meteorTexPath;
5923
+ if (meteorTexPath) manifest.meteorTex = resourceUrl(meteorTexPath);
5918
5924
  const sparkleTexPath = allTex.find((p) => p.toLowerCase().includes("sparkle") || p.toLowerCase().includes("halo") || p.toLowerCase().includes("star"));
5919
- if (sparkleTexPath) manifest.sparkleTex = resourceBase + sparkleTexPath;
5925
+ if (sparkleTexPath) manifest.sparkleTex = resourceUrl(sparkleTexPath);
5920
5926
  const sceneObjects = scene.objects;
5921
5927
  const resolveObjectTransform = (obj) => {
5922
5928
  const chain = [obj];
@@ -5987,7 +5993,7 @@ function buildSceneManifestVia(access, token, projectOverride) {
5987
5993
  if (reflTex) manifest.layers.push({
5988
5994
  name: "Reflection",
5989
5995
  isReflection: true,
5990
- texUrl: resourceBase + reflTex,
5996
+ texUrl: resourceUrl(reflTex),
5991
5997
  x: width / 2,
5992
5998
  y: height / 2,
5993
5999
  w: width,
@@ -6095,7 +6101,7 @@ function buildSceneManifestVia(access, token, projectOverride) {
6095
6101
  const alpha = typeof obj.alpha === "number" && Number.isFinite(obj.alpha) ? Math.min(1, Math.max(0, obj.alpha)) : 1;
6096
6102
  let videoUrl;
6097
6103
  try {
6098
- if (parseTexInternal(file.bytes).isVideoMp4) videoUrl = resourceBase + texPath;
6104
+ if (parseTexInternal(file.bytes).isVideoMp4) videoUrl = resourceUrl(texPath);
6099
6105
  } catch {}
6100
6106
  let uvCrop;
6101
6107
  if (decoded && typeof modelJson.width === "number" && typeof modelJson.height === "number") {
@@ -6118,7 +6124,7 @@ function buildSceneManifestVia(access, token, projectOverride) {
6118
6124
  if (reflTex) manifest.layers.push({
6119
6125
  name: "Reflection",
6120
6126
  isReflection: true,
6121
- texUrl: resourceBase + reflTex,
6127
+ texUrl: resourceUrl(reflTex),
6122
6128
  x: layerX,
6123
6129
  y: layerY,
6124
6130
  w: lw,
@@ -6128,7 +6134,7 @@ function buildSceneManifestVia(access, token, projectOverride) {
6128
6134
  }
6129
6135
  manifest.layers.push({
6130
6136
  name: typeof obj.name === "string" ? obj.name : "layer",
6131
- texUrl: resourceBase + texPath,
6137
+ texUrl: resourceUrl(texPath),
6132
6138
  x: layerX,
6133
6139
  y: layerY,
6134
6140
  w: lw,
@@ -6137,7 +6143,7 @@ function buildSceneManifestVia(access, token, projectOverride) {
6137
6143
  angle: objAngles[2] || 0,
6138
6144
  uvCrop,
6139
6145
  shader: layerShader,
6140
- texUrls: texPaths.length > 1 ? texPaths.map((p) => resourceBase + p) : void 0,
6146
+ texUrls: texPaths.length > 1 ? texPaths.map((p) => resourceUrl(p)) : void 0,
6141
6147
  userColors: layerUserColors,
6142
6148
  nums: Object.keys(nums).length > 0 ? nums : void 0,
6143
6149
  isGround,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@linxin666/dsh-client-ui-skin-center",
3
3
  "description": "In-GUI skin center for the dsh web GUI — the single skin package: skins are pure asset directories (built-in plus $DSH_HOME/skins) loaded and rendered only by the skin center; live try-on and one-click apply switch atomically in the page (no reload, no cordis.patch.yml rewrite, no restart)",
4
- "version": "0.3.19",
4
+ "version": "0.3.21",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "dsh": {
14
14
  "engines": {
15
- "dsh": ">=0.1.2-rc.1"
15
+ "dsh": ">=0.1.5-rc.1"
16
16
  },
17
17
  "bundle": {
18
18
  "patch": "./cordis.patch.yml"
@@ -39,15 +39,15 @@
39
39
  },
40
40
  "devDependencies": {
41
41
  "@deepseek-ai/cordis": "^4.0.2",
42
- "@deepseek-ai/dsh-api-remotes": "^0.1.2-rc.1",
43
- "@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-rc.1",
44
- "@deepseek-ai/dsh-client-locale": "^0.1.2-rc.1",
45
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-rc.1",
46
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-rc.1",
47
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-rc.1",
48
- "@deepseek-ai/dsh-client-ui-theme": "^0.1.2-rc.1",
49
- "@deepseek-ai/dsh-host-webserver": "^0.1.2-rc.1",
50
- "@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
42
+ "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.1",
43
+ "@deepseek-ai/dsh-api-workspace-controller": "^0.1.5-rc.1",
44
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.1",
45
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.1",
46
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.1",
47
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.1",
48
+ "@deepseek-ai/dsh-client-ui-theme": "^0.1.5-rc.1",
49
+ "@deepseek-ai/dsh-host-webserver": "^0.1.5-rc.1",
50
+ "@deepseek-ai/dsh-settings": "^0.1.5-rc.1",
51
51
  "@types/jpeg-js": "^0.3.7",
52
52
  "@types/node": "^22.20.0",
53
53
  "@types/react": "^18.3.0",
@@ -260,3 +260,372 @@ body[data-ds-dark-theme] [data-phase="active"] [class*="composerSeat"]:before {
260
260
  backdrop-filter: blur(6px);
261
261
  background: linear-gradient(#0000 0, #101624d1 36px);
262
262
  }
263
+
264
+ /**
265
+ * Blue Fantasy — text readability layer (L3).
266
+ *
267
+ * The whale illustration sits directly behind the conversation, so body text,
268
+ * tool output and the turn-tail controls lose contrast against the art. This
269
+ * layer paints a translucent frosted plate *behind the text only* (shrink to
270
+ * fit on block elements), leaving the illustration visible everywhere else.
271
+ *
272
+ * - Markdown blocks, lists, code and tables: one plate per block.
273
+ * - Disclosure rows (think / bash / context injection / system prompt):
274
+ * one plate per row, clipped to the content width.
275
+ * - The shimmering turn-status line keeps its gradient — the plate is painted
276
+ * by a ::before layer instead of the element's own background.
277
+ * - Expanded bodies (command output / reasoning / injected context) stay
278
+ * full-width so long output keeps its measure.
279
+ * - Turn-tail controls (copy / feedback / regenerate) sit on a plate so the
280
+ * icons stay legible where the art is busy.
281
+ * - Tool-call rows (bash / custom renderers) plate their outermost wrapper and
282
+ * reset the two layers nested inside it, so a row is one plate and not three.
283
+ * - The user's own message bubble keeps its accent colour and gains a
284
+ * translucent frosted body whose density rides the same slider.
285
+ *
286
+ * Both knobs come from the skin-center Background card and are read as optional
287
+ * variables with fallbacks: the blur is var(--dsh-skin-bubble-blur, 10px), and
288
+ * every text plate alpha rides var(--dsh-skin-bubble-alpha, .5). Each plate keeps
289
+ * its own ratio so the default 50% reproduces the values this layer first shipped
290
+ * with. The "Bubble opacity" slider therefore drives everything painted *behind
291
+ * text* -- markdown blocks, tool output, the turn-process row -- and nothing else.
292
+ * The window chrome (header, right sidebar) is deliberately not on that slider:
293
+ * it is frame, not bubble, and it keeps a fixed density.
294
+ *
295
+ * The light base is the left pane's measured rgb(242 245 250) (dark:
296
+ * rgb(16 22 42)) instead of pure white, so the plates sit in the same colour
297
+ * family as the panels around them.
298
+ */
299
+
300
+ /* --- markdown: one plate per block ---------------------------------------- */
301
+
302
+ [class*="markdown"] > p,
303
+ [class*="markdown"] > :where(h1, h2, h3, h4, h5, h6),
304
+ [class*="markdown"] > blockquote {
305
+ background: rgb(242 245 250 / calc(var(--dsh-skin-bubble-alpha, .5) * 1));
306
+ -webkit-backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
307
+ backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
308
+ border-radius: 6px;
309
+ padding: 2px 7px;
310
+ width: fit-content;
311
+ max-width: 100%;
312
+ box-decoration-break: clone;
313
+ -webkit-box-decoration-break: clone;
314
+ box-shadow: 0 1px 3px rgba(28, 37, 70, 0.08);
315
+ }
316
+
317
+ /* One plate for the whole list: the markers sit outside the ul/ol box, and a
318
+ plate per item would leave the markers on the bare illustration. */
319
+ [class*="markdown"] > :where(ul, ol) {
320
+ background: rgb(242 245 250 / calc(var(--dsh-skin-bubble-alpha, .5) * 1));
321
+ -webkit-backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
322
+ backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
323
+ border-radius: 8px;
324
+ padding: 6px 12px 6px 26px;
325
+ width: fit-content;
326
+ max-width: 100%;
327
+ box-shadow: 0 1px 3px rgba(28, 37, 70, 0.08);
328
+ }
329
+
330
+ [class*="markdown"] > pre {
331
+ background: rgb(242 245 250 / calc(var(--dsh-skin-bubble-alpha, .5) * 1));
332
+ -webkit-backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
333
+ backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
334
+ border-radius: 6px;
335
+ padding: 3px 7px;
336
+ box-shadow: 0 1px 3px rgba(28, 37, 70, 0.08);
337
+ }
338
+
339
+ /* A table renders inside a scroll wrapper, so it is not a direct child of the
340
+ markdown box; plate the wrapper to frost the table as a whole.
341
+
342
+ The wrapper is a block and stretches to the column by default, so a table
343
+ narrower than the column leaves the plate hanging ~300px past the last cell --
344
+ a slab of empty glass next to prose plates that all hug their text. Shrink the
345
+ plate to its table (still capped at the column) and centre it. Wide tables are
346
+ unaffected: max-width keeps the plate inside the column and the wrapper's own
347
+ overflow-x:auto still scrolls the table itself. */
348
+ [class*="markdown"] [class*="tableScroll"] {
349
+ background: rgb(242 245 250 / calc(var(--dsh-skin-bubble-alpha, .5) * 1.1));
350
+ -webkit-backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
351
+ backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
352
+ border-radius: 10px;
353
+ padding: 8px 10px;
354
+ box-shadow: 0 1px 4px rgba(28, 37, 70, 0.08);
355
+ width: fit-content;
356
+ max-width: 100%;
357
+ margin-left: auto;
358
+ margin-right: auto;
359
+ }
360
+
361
+ /* The shell ships two table shapes: the default hugs its content
362
+ (`.tableScroll table { width: max-content }`) and a fill variant stretches to
363
+ the column (`.tableFill table { width: 100% }`). Tables here were landing on
364
+ the fill variant, so a four-column table spanned the whole column while every
365
+ prose plate above it hugged its text -- the layer contradicted itself, and a
366
+ short table read as a slab of empty plate.
367
+
368
+ Put the fill variant back on the content-hugging shape. Wide tables still
369
+ behave: the wrapper keeps max-width:100% and overflow-x:auto, so an
370
+ over-long table scrolls instead of overflowing. */
371
+ [class*="markdown"] [class*="tableFill"] table {
372
+ width: max-content;
373
+ max-width: 100%;
374
+ }
375
+
376
+ body[data-ds-dark-theme] [class*="markdown"] > p,
377
+ body[data-ds-dark-theme] [class*="markdown"] > :where(h1, h2, h3, h4, h5, h6),
378
+ body[data-ds-dark-theme] [class*="markdown"] > blockquote,
379
+ body[data-ds-dark-theme] [class*="markdown"] > :where(ul, ol),
380
+ body[data-ds-dark-theme] [class*="markdown"] > pre {
381
+ background: rgb(16 22 42 / calc(var(--dsh-skin-bubble-alpha, .5) * .8));
382
+ }
383
+
384
+ body[data-ds-dark-theme] [class*="markdown"] [class*="tableScroll"] {
385
+ background: rgb(16 22 42 / calc(var(--dsh-skin-bubble-alpha, .5) * .9));
386
+ }
387
+
388
+ /* --- disclosure rows: one plate per row, clipped to the text -------------- */
389
+
390
+ [data-slot="conversation.session"] [data-disclosure-row] {
391
+ background: rgb(242 245 250 / calc(var(--dsh-skin-bubble-alpha, .5) * 1));
392
+ -webkit-backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
393
+ backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
394
+ border-radius: 8px;
395
+ padding: 1px 8px;
396
+ width: fit-content;
397
+ max-width: 100%;
398
+ }
399
+
400
+ body[data-ds-dark-theme] [data-slot="conversation.session"] [data-disclosure-row] {
401
+ background: rgb(16 22 42 / calc(var(--dsh-skin-bubble-alpha, .5) * .8));
402
+ }
403
+
404
+ /* --- turn status: keep the shimmer, plate behind the text ----------------- */
405
+
406
+ /* The status line paints its own background with background-clip: text, so it
407
+ cannot take a plate directly; a ::before layer frosts the art underneath
408
+ while the gradient stays on the text. The turn-status clock chip embeds the
409
+ same substring, hence the explicit exclusion. */
410
+ [class*="turnStatus"]:not([class*="turnStatusClock"]) {
411
+ position: relative;
412
+ border-radius: 8px;
413
+ }
414
+
415
+ [class*="turnStatus"]:not([class*="turnStatusClock"])::before {
416
+ content: "";
417
+ position: absolute;
418
+ inset: -5px -10px;
419
+ z-index: -1;
420
+ background: rgb(242 245 250 / calc(var(--dsh-skin-bubble-alpha, .5) * 1.1));
421
+ -webkit-backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
422
+ backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
423
+ border-radius: 8px;
424
+ }
425
+
426
+ body[data-ds-dark-theme] [class*="turnStatus"]:not([class*="turnStatusClock"])::before {
427
+ background: rgb(16 22 42 / calc(var(--dsh-skin-bubble-alpha, .5) * 1));
428
+ }
429
+
430
+ /* --- turn-tail controls: copy / feedback / regenerate --------------------- */
431
+
432
+ [data-turn-tail] [class*="actions"],
433
+ [data-chat-flow-kind="user"] [class*="actions"],
434
+ [data-chat-flow-kind="steering"] [class*="actions"] {
435
+ background: rgb(242 245 250 / calc(var(--dsh-skin-bubble-alpha, .5) * 1));
436
+ -webkit-backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
437
+ backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
438
+ border-radius: 10px;
439
+ padding: 0 8px;
440
+ width: fit-content;
441
+ }
442
+
443
+ body[data-ds-dark-theme] [data-turn-tail] [class*="actions"],
444
+ body[data-ds-dark-theme] [data-chat-flow-kind="user"] [class*="actions"],
445
+ body[data-ds-dark-theme] [data-chat-flow-kind="steering"] [class*="actions"] {
446
+ background: rgb(16 22 42 / calc(var(--dsh-skin-bubble-alpha, .5) * .8));
447
+ }
448
+
449
+ /* --- expanded bodies: full width, plate only ----------------------------- */
450
+
451
+ [class*="_5OnbHa_body"],
452
+ [class*="lcKema_thinkBody"],
453
+ [class*="XrJvXW_body"],
454
+ [data-context-injection-body],
455
+ [data-system-prompt-body] {
456
+ background: rgb(242 245 250 / calc(var(--dsh-skin-bubble-alpha, .5) * 1));
457
+ -webkit-backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
458
+ backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
459
+ border-radius: 8px;
460
+ }
461
+
462
+ body[data-ds-dark-theme] [class*="_5OnbHa_body"],
463
+ body[data-ds-dark-theme] [class*="lcKema_thinkBody"],
464
+ body[data-ds-dark-theme] [class*="XrJvXW_body"],
465
+ body[data-ds-dark-theme] [data-context-injection-body],
466
+ body[data-ds-dark-theme] [data-system-prompt-body] {
467
+ background: rgb(16 22 42 / calc(var(--dsh-skin-bubble-alpha, .5) * .8));
468
+ }
469
+
470
+ /* --- fenced code: the shell paints a plate but no frosting ---------------- */
471
+
472
+ /* A fenced block renders through the shell as a <div class="md-code-block">, not
473
+ a <pre>, so the markdown block rule above never reached it. It already carries
474
+ the shell's own plate (rgba(243 245 251 / .58) light, and its banner + <pre>
475
+ rows repeat it), but with no backdrop-filter the art stays sharp underneath
476
+ and the block reads flatter than the prose around it. Frost it and leave the
477
+ shell's fill alone. */
478
+ [class*="markdown"] [class*="md-code-block"] {
479
+ -webkit-backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
480
+ backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
481
+ }
482
+
483
+
484
+ /* --- turn-process row: one plate, and only one ---------------------------- */
485
+
486
+ /* The "N tool calls . M messages" row is one module whose three slots share a
487
+ single class prefix: the button, its label span and its chevron. Painting the
488
+ prefix lands on all three, the .5 plates stack, and the row reads as a double
489
+ background. The button carries a stable semantic attribute (data-turn-process,
490
+ stamped by TurnProcessNodeView with its turn number), so target that: an
491
+ attribute selector matches the name exactly and leaves the -member / -hidden /
492
+ -answer / -inline siblings alone.
493
+
494
+ Upstream that row is a separator (height 33px, padding 0 0 8px, a .5px bottom
495
+ border). Inside a plate the 8px reads as dead space with a stray line across
496
+ the rounded corner, so square it up with the tool rows and hand the separator
497
+ back only while expanded. */
498
+ [data-slot="main.conversation"] [data-turn-process] {
499
+ background: rgb(242 245 250 / calc(var(--dsh-skin-bubble-alpha, .5) * 1));
500
+ -webkit-backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
501
+ backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
502
+ border-radius: 8px;
503
+ height: auto;
504
+ padding: 1px 8px;
505
+ border-bottom: none;
506
+ width: fit-content;
507
+ max-width: 100%;
508
+ margin-left: auto;
509
+ margin-right: auto;
510
+ }
511
+
512
+ body[data-ds-dark-theme] [data-slot="main.conversation"] [data-turn-process] {
513
+ background: rgb(16 22 42 / calc(var(--dsh-skin-bubble-alpha, .5) * .8));
514
+ }
515
+
516
+ [data-slot="main.conversation"] [data-turn-process][data-open] {
517
+ border-bottom: .5px solid var(--dsw-alias-border-l2);
518
+ }
519
+
520
+ /* --- chrome: the top bar and the right sidebar ---------------------------- */
521
+
522
+ /* The three columns only read as one surface if they share a base, so the
523
+ conversation header and the right sidebar reuse the left pane's measured
524
+ rgb(242 245 250) at the same density instead of plain white. The density is
525
+ fixed rather than tied to --dsh-skin-bubble-alpha: this is window chrome, and
526
+ dragging the Bubble opacity slider should fade bubbles, not the frame.
527
+
528
+ The right sidebar renders as [class*="rightbarCol"] > [data-slot="rightbar"]
529
+ > [data-slot="rightbar.session"] > [class*="panel"]. The two slot hosts are
530
+ display:contents and measure 0x0, so a background on them paints nothing --
531
+ the plate has to land on .panel.
532
+
533
+ The items inside the header (session name, mode picker, the tabs) stay
534
+ background-free on purpose: plates under those labels read as smudges and
535
+ fight the official underline indicator. */
536
+ [data-slot="main.conversation"] header,
537
+ [data-slot="rightbar.session"] > *,
538
+ [class*="rightbarCol"] [class*="panel"] {
539
+ background: rgba(242, 245, 250, 0.75);
540
+ }
541
+
542
+ [data-slot="main.conversation"] header {
543
+ border-bottom: 1px solid rgba(28, 37, 70, 0.08);
544
+ }
545
+
546
+ body[data-ds-dark-theme] [data-slot="main.conversation"] header,
547
+ body[data-ds-dark-theme] [data-slot="rightbar.session"] > *,
548
+ body[data-ds-dark-theme] [class*="rightbarCol"] [class*="panel"] {
549
+ background: rgba(16, 22, 42, 0.75);
550
+ }
551
+
552
+ body[data-ds-dark-theme] [data-slot="main.conversation"] header {
553
+ border-bottom-color: rgba(160, 180, 230, 0.12);
554
+ }
555
+
556
+
557
+ /* --- tool-call rows: one plate per row ------------------------------------ */
558
+
559
+ /* A tool call renders as a wrapper with two near-identical layers inside it
560
+ (the row body and its disclosure header). Plating all three stacks the same
561
+ translucent fill three times and the row reads as a doubled background, so
562
+ plate the wrapper only and reset what is nested inside it. The wrapper
563
+ carries a stable callRow suffix; the inner reset is scoped to the row and
564
+ repeats the dark-theme prefix so it outranks the dark disclosure rule. */
565
+ [data-slot="main.conversation"] [class*="callRow"] {
566
+ background: rgb(242 245 250 / calc(var(--dsh-skin-bubble-alpha, .5) * 1));
567
+ -webkit-backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
568
+ backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
569
+ border-radius: 8px;
570
+ padding: 1px 8px;
571
+ width: fit-content;
572
+ max-width: 100%;
573
+ }
574
+
575
+ body[data-ds-dark-theme] [data-slot="main.conversation"] [class*="callRow"] {
576
+ background: rgb(16 22 42 / calc(var(--dsh-skin-bubble-alpha, .5) * .8));
577
+ }
578
+
579
+ [data-slot="conversation.session"] [class*="callRow"] [class*="o3BgMG_root"],
580
+ [data-slot="conversation.session"] [class*="callRow"] [class*="o3BgMG_summary"],
581
+ [data-slot="conversation.session"] [class*="callRow"] [data-disclosure-row],
582
+ body[data-ds-dark-theme] [data-slot="conversation.session"] [class*="callRow"] [class*="o3BgMG_root"],
583
+ body[data-ds-dark-theme] [data-slot="conversation.session"] [class*="callRow"] [class*="o3BgMG_summary"],
584
+ body[data-ds-dark-theme] [data-slot="conversation.session"] [class*="callRow"] [data-disclosure-row] {
585
+ background: none;
586
+ -webkit-backdrop-filter: none;
587
+ backdrop-filter: none;
588
+ border-radius: 0;
589
+ box-shadow: none;
590
+ padding: 0;
591
+ margin: 0;
592
+ width: auto;
593
+ max-width: none;
594
+ }
595
+
596
+ /* --- the user's own message bubble ---------------------------------------- */
597
+
598
+ /* Keep the accent fill the skin already assigns, add translucency to it and
599
+ frost what shows through. The compound selector matters: a bare
600
+ [class*="bubble"] would also catch unrelated bubbles elsewhere in the shell. */
601
+ [data-slot="main.conversation"] [class*="userStack"] [class*="bubble"] {
602
+ /* 120% of the slider reproduces the 60% body this layer shipped with. */
603
+ background: color-mix(in srgb, var(--dsw-specific-bubble) calc(var(--dsh-skin-bubble-alpha, .5) * 120%), transparent);
604
+ -webkit-backdrop-filter: blur(var(--dsh-skin-bubble-blur, 12px)) saturate(1.3);
605
+ backdrop-filter: blur(var(--dsh-skin-bubble-blur, 12px)) saturate(1.3);
606
+ }
607
+
608
+ /* --- deliverables row (this turn's produced files) ------------------------ */
609
+
610
+ /* Sits in the turn tail and had no plate at all.
611
+
612
+ The width cannot come from a bare fit-content: the row's parent is a
613
+ display:contents slot host that measures 0, and fit-content plus max-width
614
+ then crushes the file chips onto three lines. Making the row an inline-level
615
+ shrink-to-fit box keeps it independent of the parent's width, and align-self
616
+ stops a flex parent from stretching it. */
617
+ [data-slot="main.conversation"] [class*="producedRow"] {
618
+ display: inline-flex;
619
+ align-self: flex-start;
620
+ width: fit-content;
621
+ max-width: 100%;
622
+ background: rgb(242 245 250 / calc(var(--dsh-skin-bubble-alpha, .5) * 1));
623
+ -webkit-backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
624
+ backdrop-filter: blur(var(--dsh-skin-bubble-blur, 10px)) saturate(1.3);
625
+ border-radius: 8px;
626
+ padding: 4px 10px;
627
+ }
628
+
629
+ body[data-ds-dark-theme] [data-slot="main.conversation"] [class*="producedRow"] {
630
+ background: rgb(16 22 42 / calc(var(--dsh-skin-bubble-alpha, .5) * .8));
631
+ }