review: theme-owned drop mask token, overlay fade-in, stable gallery keys, close-button stacking, stale note defaults

This commit is contained in:
creatixchu
2026-08-12 13:12:06 +08:00
parent 7259252536
commit c9a536e0c8
10 changed files with 33 additions and 12 deletions

View File

@@ -981,6 +981,9 @@ function projectionValuesOf(log: readonly SessionEvent[]): Record<string, unknow
values['contextBreakdown'] = contextBreakdownOf(log)
// Always present (attachment service composed): the deployment image
// limits, constant per boot (mirrors the attachment-local defaults).
// Deliberate host divergence: the real gateway never pushes an imageLimits
// change frame (constant unit), but the fixture's uniform baseline replay
// frames every key here, incidentally exercising higher-seq-wins.
values['imageLimits'] = {
maxImageBytes: 10 * 1024 * 1024,
maxImagesPerMessage: 20,

View File

@@ -1,6 +1,7 @@
/* Full-viewport drop invitation (DeepSeek Chat DragMask). pointer-events:
none — the layer is decoration; drag events must keep hitting the page so
the owner's enter/leave count stays balanced. */
the owner's enter/leave count stays balanced. The frosted sheet color is
the theme's drop-mask alias (dark override lives with the theme owner). */
.mask {
position: fixed;
inset: 0;
@@ -9,12 +10,20 @@
align-items: center;
justify-content: center;
pointer-events: none;
background-color: rgba(255, 255, 255, 0.7);
background-color: var(--dsw-alias-bg-mask-drop);
backdrop-filter: blur(10px);
animation: fade-in 160ms ease-out;
}
:global(body[data-ds-dark-theme]) .mask {
background-color: rgba(39, 39, 48, 0.7);
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
.mask {
animation: none;
}
}
.wrap {

View File

@@ -1,5 +1,4 @@
import { createPortal } from 'react-dom'
import clsx from 'clsx'
import css from './DropOverlay.module.css'
/** Drop-overlay strings the owner resolves from its own locale namespace. */
@@ -27,7 +26,7 @@ export function DropOverlay({ disabled, labels }: {
labels: DropOverlayLabels
}) {
return createPortal(
<div className={clsx(css.mask, disabled && css.disabled)} role="status">
<div className={css.mask} role="status">
<div className={css.wrap}>
<div className={css.illustration} aria-hidden="true">
{disabled ? <UploadDisabledIllustration /> : <UploadIllustration />}

View File

@@ -19,7 +19,6 @@
.image {
position: relative;
z-index: 1;
max-width: min(100%, 1600px);
max-height: calc(100vh - 80px);
object-fit: contain;
@@ -32,6 +31,7 @@
position: fixed;
top: 20px;
right: 20px;
z-index: 1;
display: grid;
place-items: center;
width: 36px;

View File

@@ -71,6 +71,10 @@ export const AssistantMarkdown = memo(function AssistantMarkdown({
case 'image': {
// Consecutive image blocks share one gallery so several images tile
// into rows instead of each opening a one-image group of its own.
// Keyed by the group's FIRST block index: a streaming append that
// extends the group then only grows `images` instead of remounting
// the gallery under a shifted key.
const start = i
const group = [block]
while (i + 1 < blocks.length) {
const next = blocks[i + 1]
@@ -78,7 +82,7 @@ export const AssistantMarkdown = memo(function AssistantMarkdown({
group.push(next)
i += 1
}
rendered.push(<ImageGallery key={i} images={group} load={imageLoader} align="start" labels={messageImageLabels(t)} />)
rendered.push(<ImageGallery key={start} images={group} load={imageLoader} align="start" labels={messageImageLabels(t)} />)
break
}
// Grouped into tool rows by ChatView; hasVisible above skips an empty shell.

View File

@@ -162,6 +162,7 @@ body {
--dsw-alias-bg-mask-2: rgba(0, 0, 0, 0.12);
--dsw-alias-bg-mask-3: rgba(0, 0, 0, 0.48);
--dsw-alias-bg-mask-photo: rgba(0, 0, 0, 0.88);
--dsw-alias-bg-mask-drop: rgba(255, 255, 255, 0.7);
--dsw-alias-bg-module-platform: var(--dsw-static-neutral-bluish-60);
--dsw-alias-bg-multi-select: var(--dsw-static-neutral-bluish-60);
--dsw-alias-bg-overlay: var(--dsw-static-neutral-bluish-150);
@@ -253,6 +254,7 @@ body[data-ds-dark-theme] {
--dsw-alias-bg-mask-2: rgba(0, 0, 0, 0.2);
--dsw-alias-bg-mask-3: rgba(0, 0, 0, 0.48);
--dsw-alias-bg-mask-photo: rgba(0, 0, 0, 0.88);
--dsw-alias-bg-mask-drop: rgba(39, 39, 48, 0.7);
--dsw-alias-bg-module-platform: var(--dsw-static-neutral-bluish-800);
--dsw-alias-bg-multi-select: var(--dsw-static-neutral-850);
--dsw-alias-bg-overlay: var(--dsw-static-neutral-bluish-700);

View File

@@ -1237,6 +1237,10 @@ export function createApiProxy(ctx: Context, defaults: ApiProxyDefaults): ApiPro
// seam package cannot reference the projection registry without a cycle,
// and the per-message rules the value describes are this proxy's own
// admission checks. The child activates only while both seams are composed.
// `view` reading the live service instead of the (null) state is sanctioned
// exactly for boot-constant units: the value cannot change within a process
// lifetime, so the fold stays observationally pure, and a stale persisted
// cache row re-viewing to the current config is the correct outcome.
ctx.inject(['sessionProjections', 'attachments'], (projectionCtx) => {
projectionCtx.sessionProjections.register<'imageLimits', null>({
key: 'imageLimits',