review: theme-owned drop mask token, overlay fade-in, stable gallery keys, close-button stacking, stale note defaults
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 />}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user