mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-10-01 22:14:51 +02:00
[eric] canvas: cut the evictable full-window texture 98MB to 43MB, so the wash has far less left to drop as a band
This commit is contained in:
@@ -32,7 +32,7 @@ import { findBrowserByWebContentsId } from '@/shared/browserRegistry';
|
||||
import { byPreviewRecency } from '@/shared/previewOrder';
|
||||
import { useClaudeTokens, useThemeAccent, useThemeWash } from '@/shared/styles/ThemeContext';
|
||||
import SpacesStrip from '@/app/pages/Dashboard/desktop/SpacesStrip';
|
||||
import { washOpaqueBackgroundUrl, washUnderlayColor, effectiveWashStops } from '@/shared/styles/washBackground';
|
||||
import { washBackgroundLayers, washUnderlayColor, effectiveWashStops } from '@/shared/styles/washBackground';
|
||||
import { useGrainTileUrl } from '@/shared/styles/useGrainTileUrl';
|
||||
import { ErrorSlime } from '@/app/components/feedback/ErrorSlime';
|
||||
|
||||
@@ -343,6 +343,14 @@ const AppShell: React.FC = () => {
|
||||
// unpinned fullscreen keeps the hover-peek overlay.
|
||||
const [fsSidebarPinned, setFsSidebarPinned] = useState(false);
|
||||
const sidebarAway = (sidebarCollapsed || (fsActive && !fsSidebarPinned)) && isDashboardViewActive;
|
||||
// The dashboard canvas paints the identical wash and grain over everything but a 6px frame of
|
||||
// shell, so painting them here too just buys a second full-window texture for the compositor to
|
||||
// evict. Skip it while the canvas is up; on every other route the shell is the only painter.
|
||||
const shellWashLayers = React.useMemo(
|
||||
() => (fsWashStops
|
||||
? washBackgroundLayers(fsWashStops, themeWashOpacity, c.bg.page, isDashboardViewActive ? null : shellGrainUrl)
|
||||
: null),
|
||||
[fsWashStops, themeWashOpacity, c.bg.page, shellGrainUrl, isDashboardViewActive]);
|
||||
// When the sidebar docks away, the canvas runs flush to the window's left edge, so the floating
|
||||
// dashboard header would sit right under the macOS traffic lights. Publish an inset the header reads
|
||||
// (only on macOS, where the lights exist) so it clears them; the sidebar carries its own clearance.
|
||||
@@ -353,6 +361,15 @@ const AppShell: React.FC = () => {
|
||||
else root.style.removeProperty('--osw-header-inset');
|
||||
return () => { root.style.removeProperty('--osw-header-inset'); };
|
||||
}, [sidebarAway]);
|
||||
// Hand the boot paint back. index.html puts the wash gradient on <html> so a reload never flashes
|
||||
// white, but nothing ever took it off: it stayed for the whole session as a full-window,
|
||||
// background-attachment:fixed root layer that the shell already paints over. That is a permanently
|
||||
// resident evictable texture bought for the first frame only, and evicting it is what shows the
|
||||
// hard-edged band. The shell covers the viewport by the time this runs, so a flat colour is all the
|
||||
// backdrop that is left to want.
|
||||
useEffect(() => {
|
||||
document.documentElement.style.background = c.bg.page;
|
||||
}, [c.bg.page]);
|
||||
// Global text-size ratio (Settings > Interface). Scaling the root font-size scales every rem-based
|
||||
// size in one shot, so type grows or shrinks together with no layout breakage. Clamped to a sane band
|
||||
// so a corrupt value can never wreck the whole UI.
|
||||
@@ -417,11 +434,11 @@ const AppShell: React.FC = () => {
|
||||
// sliver of shell peeking past the viewport reads as continuous texture, never a tint/grain seam.
|
||||
...(fsWashStops ? {
|
||||
backgroundColor: washUnderlayColor(fsWashStops, themeWashOpacity, c.bg.page),
|
||||
backgroundImage: shellGrainUrl
|
||||
? `${shellGrainUrl}, ${washOpaqueBackgroundUrl(fsWashStops, themeWashOpacity, c.bg.page)}`
|
||||
: washOpaqueBackgroundUrl(fsWashStops, themeWashOpacity, c.bg.page),
|
||||
backgroundSize: shellGrainUrl ? 'auto, 100% 100%' : '100% 100%',
|
||||
backgroundRepeat: shellGrainUrl ? 'repeat, no-repeat' : 'no-repeat',
|
||||
...(shellWashLayers ? {
|
||||
backgroundImage: shellWashLayers.image,
|
||||
backgroundSize: shellWashLayers.size,
|
||||
backgroundRepeat: shellWashLayers.repeat,
|
||||
} : {}),
|
||||
} : {}),
|
||||
}}>
|
||||
{/* Sidebar retired: dashboards switch via the macOS-Spaces top strip; a slim band below the
|
||||
|
||||
@@ -18,10 +18,13 @@ import ApplicationsWindow from '../desktop/ApplicationsWindow';
|
||||
import type { ClaudeTokens } from '@/shared/styles/claudeTokens';
|
||||
import { useThemeAccent, useThemeWash } from '@/shared/styles/ThemeContext';
|
||||
import { useGrainTileUrl } from '@/shared/styles/useGrainTileUrl';
|
||||
import { washOpaqueBackgroundUrl, washUnderlayColor, effectiveWashStops } from '@/shared/styles/washBackground';
|
||||
import { washBackgroundLayers, washUnderlayColor, effectiveWashStops } from '@/shared/styles/washBackground';
|
||||
|
||||
// How far the dot grid bleeds past the viewport; must exceed one tile period (24px * max zoom) so the compositor phase translate can never expose an edge.
|
||||
const GRID_BLEED_PX = 256;
|
||||
// How far the dot grid bleeds past the viewport. The phase translate is `pan % dotSpacing`, so it
|
||||
// can never exceed one tile period; deriving the bleed from that bound keeps the layer as small as
|
||||
// it can be (a hardcoded 256 made it 3.5x bigger than needed, all of it evictable texture) and a
|
||||
// future max-zoom bump can't silently uncover an edge.
|
||||
const GRID_BLEED_PX = 24 * MAX_ZOOM;
|
||||
import type { AgentSession } from '@/shared/state/agentsSlice';
|
||||
import type {
|
||||
CardPosition,
|
||||
@@ -32,7 +35,7 @@ import type {
|
||||
} from '@/shared/state/dashboardLayoutSlice';
|
||||
import type { Output } from '@/shared/state/outputsSlice';
|
||||
import type { CardType, useDashboardSelection } from '../hooks/state/useDashboardSelection';
|
||||
import type { useCanvasControls } from '../hooks/interaction/useCanvasControls';
|
||||
import { MAX_ZOOM, type useCanvasControls } from '../hooks/interaction/useCanvasControls';
|
||||
import { useWebviewSuspend } from '../hooks/interaction/useWebviewSuspend';
|
||||
import { deleteSelectedCards } from '../hooks/interaction/deleteSelectedCards';
|
||||
import { getLastInteractedBrowser } from '@/shared/browserFocus';
|
||||
@@ -174,8 +177,8 @@ const DashboardCanvas: React.FC<DashboardCanvasProps> = ({
|
||||
const dotSpacing = 24 * canvas.zoom;
|
||||
// Memoized: this component re-renders every card-drag frame, and rebuilding these strings (SVG encode + hex blends) per frame is pure waste.
|
||||
const washUnderlay = React.useMemo(() => washUnderlayColor(washStops, washOpacity, c.bg.page), [washStops, washOpacity, c.bg.page]);
|
||||
const washUrl = React.useMemo(() => washOpaqueBackgroundUrl(washStops, washOpacity, c.bg.page), [washStops, washOpacity, c.bg.page]);
|
||||
const grainTileUrl = useGrainTileUrl(grain);
|
||||
const washLayers = React.useMemo(() => washBackgroundLayers(washStops, washOpacity, c.bg.page, grainTileUrl), [washStops, washOpacity, c.bg.page, grainTileUrl]);
|
||||
const gridTileUrl = React.useMemo(() => `url("data:image/svg+xml,${encodeURIComponent(
|
||||
`<svg xmlns='http://www.w3.org/2000/svg' width='${dotSpacing}' height='${dotSpacing}'><circle cx='${dotSpacing / 2}' cy='${dotSpacing / 2}' r='${dotSize}' fill='${c.border.medium}'/></svg>`,
|
||||
)}")`, [dotSpacing, dotSize, c.border.medium]);
|
||||
@@ -427,11 +430,12 @@ const DashboardCanvas: React.FC<DashboardCanvasProps> = ({
|
||||
backgroundColor: washUnderlay,
|
||||
// Wash + grain paint HERE rather than on a child: two stacked full-viewport layers meant two
|
||||
// rasters the compositor could evict independently, and a dropped one exposed the flat tint
|
||||
// as a hard-edged band. One element, one raster, one fewer thing to lose.
|
||||
...(washStops && washStops.length > 0 ? {
|
||||
backgroundImage: grainTileUrl ? `${grainTileUrl}, ${washUrl}` : washUrl,
|
||||
backgroundSize: grainTileUrl ? 'auto, 100% 100%' : '100% 100%',
|
||||
backgroundRepeat: grainTileUrl ? 'repeat, no-repeat' : 'no-repeat',
|
||||
// as a hard-edged band. One element, one raster, one fewer thing to lose. A uniform wash
|
||||
// drops the image entirely, because backgroundColor above already IS that colour.
|
||||
...(washLayers ? {
|
||||
backgroundImage: washLayers.image,
|
||||
backgroundSize: washLayers.size,
|
||||
backgroundRepeat: washLayers.repeat,
|
||||
} : {}),
|
||||
cursor: canvas.isPanning
|
||||
? 'grabbing'
|
||||
|
||||
@@ -14,7 +14,7 @@ const MIN_ZOOM = 0.15;
|
||||
// measured 100% -> 88% -> 79% -> 61% -> 36% -> 18% over one ordinary session, at which point no word
|
||||
// on the canvas is readable. A hand-driven zoom can still go all the way to MIN_ZOOM.
|
||||
|
||||
const MAX_ZOOM = 3.0;
|
||||
export const MAX_ZOOM = 3.0;
|
||||
const ZOOM_IN_FACTOR = 1.1;
|
||||
const ZOOM_OUT_FACTOR = 1 / ZOOM_IN_FACTOR;
|
||||
const FIT_PADDING = 200;
|
||||
|
||||
Reference in New Issue
Block a user