From 702a45588317fe60dca726d3403db680c9aad046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E7=86=B1?= Date: Sat, 15 Aug 2026 09:57:15 +0800 Subject: [PATCH] revert: "fix(docs): follow theme colors on canvas surfaces" (#7515) --- .../doc-header-footer.controller.spec.ts | 47 ++++++++++++++++++- .../__tests__/doc-render-controller.spec.ts | 2 +- .../doc-header-footer.controller.ts | 36 +++++++++++++- .../src/services/doc-render-background.ts | 2 +- .../docs/__tests__/doc-background.spec.ts | 4 +- .../src/components/docs/doc-background.ts | 2 +- .../__tests__/canvas-color.service.spec.ts | 4 -- .../src/services/canvas-color.service.ts | 13 ----- 8 files changed, 85 insertions(+), 25 deletions(-) diff --git a/packages/docs-ui/src/controllers/__tests__/doc-header-footer.controller.spec.ts b/packages/docs-ui/src/controllers/__tests__/doc-header-footer.controller.spec.ts index 827d0e20bc..54a4eace01 100644 --- a/packages/docs-ui/src/controllers/__tests__/doc-header-footer.controller.spec.ts +++ b/packages/docs-ui/src/controllers/__tests__/doc-header-footer.controller.spec.ts @@ -22,7 +22,7 @@ import { LocaleService, } from '@univerjs/core'; import { DocSkeletonManagerService, RichTextEditingMutation } from '@univerjs/docs'; -import { DocumentEditArea, IRenderManagerService, Path } from '@univerjs/engine-render'; +import { DocumentEditArea, IRenderManagerService, Path, Rect } from '@univerjs/engine-render'; import { BehaviorSubject, Subject } from 'rxjs'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { CloseHeaderFooterCommand } from '../../commands/commands/doc-header-footer.command'; @@ -151,8 +151,46 @@ describe('DocHeaderFooterController', () => { controller.dispose(); }); - it('draws header/footer guides while editing header or footer', () => { + it('covers header and footer areas while editing the document body', () => { + const { controller, pageRender$ } = createController({ editArea: DocumentEditArea.BODY }); + const rectSpy = vi.spyOn(Rect, 'drawWith').mockImplementation(() => undefined); + const pathSpy = vi.spyOn(Path, 'drawWith').mockImplementation(() => undefined); + const textSpy = vi.spyOn(TextBubbleShape, 'drawWith').mockImplementation(() => undefined); + const ctx = createCtx(); + + pageRender$.next({ + ctx, + pageLeft: 12, + pageTop: 24, + page: { + pageWidth: 200, + pageHeight: 300, + marginTop: 30, + marginBottom: 40, + }, + }); + + expect(ctx.translate).toHaveBeenCalledWith(11.5, 23.5); + expect(rectSpy).toHaveBeenCalledTimes(2); + expect(rectSpy.mock.calls[0][1]).toMatchObject({ + width: 200, + height: 30, + fill: 'alpha(white, 0.5)', + }); + expect(rectSpy.mock.calls[1][1]).toMatchObject({ + width: 200, + height: 40, + fill: 'alpha(white, 0.5)', + }); + expect(pathSpy).not.toHaveBeenCalled(); + expect(textSpy).not.toHaveBeenCalled(); + + controller.dispose(); + }); + + it('covers the body and draws header/footer guides while editing header or footer', () => { const { controller, pageRender$ } = createController({ editArea: DocumentEditArea.HEADER }); + const rectSpy = vi.spyOn(Rect, 'drawWith').mockImplementation(() => undefined); const pathSpy = vi.spyOn(Path, 'drawWith').mockImplementation(() => undefined); const textSpy = vi.spyOn(TextBubbleShape, 'drawWith').mockImplementation(() => undefined); const ctx = createCtx(); @@ -169,6 +207,11 @@ describe('DocHeaderFooterController', () => { }, }); + expect(rectSpy).toHaveBeenCalledWith(ctx, expect.objectContaining({ + top: 30, + width: 200, + height: 230, + })); expect(pathSpy).toHaveBeenCalledTimes(2); expect(pathSpy).toHaveBeenCalledWith(ctx, expect.objectContaining({ stroke: 'primary.600' })); expect(textSpy).toHaveBeenCalledWith(ctx, expect.objectContaining({ diff --git a/packages/docs-ui/src/controllers/__tests__/doc-render-controller.spec.ts b/packages/docs-ui/src/controllers/__tests__/doc-render-controller.spec.ts index 0f146accd0..1009ad8656 100644 --- a/packages/docs-ui/src/controllers/__tests__/doc-render-controller.spec.ts +++ b/packages/docs-ui/src/controllers/__tests__/doc-render-controller.spec.ts @@ -235,7 +235,7 @@ function createControllerFixture(options?: { describe('doc render controller', () => { it.each([ [DocumentFlavor.TRADITIONAL, 'gray.100'], - [DocumentFlavor.MODERN, 'token(white)'], + [DocumentFlavor.MODERN, 'white'], ])('resolves the %s workspace background again when dark mode changes', (documentFlavor, backgroundToken) => { const { canvasColorService, canvasElement, darkMode$ } = createControllerFixture({ documentFlavor }); canvasColorService.getRenderColor.mockImplementation((color: string) => `dark:${color}`); diff --git a/packages/docs-ui/src/controllers/doc-header-footer.controller.ts b/packages/docs-ui/src/controllers/doc-header-footer.controller.ts index 9a3946039f..7274fdadc8 100644 --- a/packages/docs-ui/src/controllers/doc-header-footer.controller.ts +++ b/packages/docs-ui/src/controllers/doc-header-footer.controller.ts @@ -41,7 +41,7 @@ import { UniverInstanceType, } from '@univerjs/core'; import { DocSkeletonManagerService, HeaderFooterType, RichTextEditingMutation } from '@univerjs/docs'; -import { DocumentEditArea, IRenderManagerService, PageLayoutType, Path, Vector2 } from '@univerjs/engine-render'; +import { DocumentEditArea, IRenderManagerService, PageLayoutType, Path, Rect, Vector2 } from '@univerjs/engine-render'; import { neoGetDocObject } from '../basics/component-tools'; import { CloseHeaderFooterCommand, CoreHeaderFooterCommand } from '../commands/commands/doc-header-footer.command'; import { IEditorService } from '../services/editor/editor-manager.service'; @@ -49,6 +49,7 @@ import { DocSelectionRenderService } from '../services/selection/doc-selection-r import { getDocPageSectionContext } from '../utils/section-header-footer'; import { TextBubbleShape } from '../views/header-footer/text-bubble'; +const HEADER_FOOTER_COVER_COLOR = 'alpha(white, 0.5)'; const HEADER_FOOTER_STROKE_COLOR = 'primary.600'; const HEADER_FOOTER_LABEL_COLOR = 'alpha(primary.600, 0.08)'; @@ -306,6 +307,7 @@ export class DocHeaderFooterController extends Disposable implements IRenderModu this.disposeWithMe( toDisposable( + // eslint-disable-next-line max-lines-per-function docsComponent.pageRender$.subscribe((config: IPageRenderConfig) => { if (this._editorService.isEditor(unitId)) { return; @@ -325,6 +327,38 @@ export class DocHeaderFooterController extends Disposable implements IRenderModu ctx.save(); ctx.translate(pageLeft - 0.5, pageTop - 0.5); + // Cover header and footer. + if (isEditBody) { + Rect.drawWith(ctx, { + left: 0, + top: 0, + width: pageWidth, + height: marginTop, + fill: HEADER_FOOTER_COVER_COLOR, + }); + ctx.save(); + ctx.translate(0, pageHeight - marginBottom); + Rect.drawWith(ctx, { + left: 0, + top: 0, + width: pageWidth, + height: marginBottom, + fill: HEADER_FOOTER_COVER_COLOR, + }); + ctx.restore(); + } else { // Cover body. + ctx.save(); + ctx.translate(0, marginTop); + Rect.drawWith(ctx, { + left: 0, + top: marginTop, + width: pageWidth, + height: pageHeight - marginTop - marginBottom, + fill: HEADER_FOOTER_COVER_COLOR, + }); + ctx.restore(); + } + if (!isEditBody) { const headerPathConfigIPathProps = { dataArray: [{ diff --git a/packages/docs-ui/src/services/doc-render-background.ts b/packages/docs-ui/src/services/doc-render-background.ts index e0b947e08c..9663ee14a2 100644 --- a/packages/docs-ui/src/services/doc-render-background.ts +++ b/packages/docs-ui/src/services/doc-render-background.ts @@ -22,7 +22,7 @@ const DOC_MODERN_WORKSPACE_BACKGROUND_COLOR = 'var(--univer-white)'; const DOC_UNSPECIFIED_WORKSPACE_BACKGROUND_COLOR = 'var(--univer-gray-100)'; const DOC_EDITOR_INTERNAL_BACKGROUND_COLOR = 'transparent'; const DOC_TRADITIONAL_WORKSPACE_BACKGROUND_TOKEN = 'gray.100'; -const DOC_MODERN_WORKSPACE_BACKGROUND_TOKEN = 'token(white)'; +const DOC_MODERN_WORKSPACE_BACKGROUND_TOKEN = 'white'; export interface IResolveDocRenderBackgroundOptions { documentFlavor?: DocumentFlavor; diff --git a/packages/engine-render/src/components/docs/__tests__/doc-background.spec.ts b/packages/engine-render/src/components/docs/__tests__/doc-background.spec.ts index bf4ccd45cc..96ba81cd54 100644 --- a/packages/engine-render/src/components/docs/__tests__/doc-background.spec.ts +++ b/packages/engine-render/src/components/docs/__tests__/doc-background.spec.ts @@ -75,7 +75,7 @@ describe('DocBackground', () => { background.draw(createCtx()); expect(rectDraw.mock.calls[0][1]).toMatchObject({ fill: 'gray.100' }); - expect(rectDraw.mock.calls[1][1]).toMatchObject({ fill: 'token(white)' }); + expect(rectDraw.mock.calls[1][1]).toMatchObject({ fill: 'white' }); background.dispose(); }); @@ -91,7 +91,7 @@ describe('DocBackground', () => { background.draw(createCtx()); expect(rectDraw).toHaveBeenCalledTimes(1); - expect(rectDraw.mock.calls[0][1]).toMatchObject({ fill: 'token(white)' }); + expect(rectDraw.mock.calls[0][1]).toMatchObject({ fill: 'white' }); background.dispose(); }); diff --git a/packages/engine-render/src/components/docs/doc-background.ts b/packages/engine-render/src/components/docs/doc-background.ts index 882eb44ffc..d0232f74bc 100644 --- a/packages/engine-render/src/components/docs/doc-background.ts +++ b/packages/engine-render/src/components/docs/doc-background.ts @@ -25,7 +25,7 @@ import { DocComponent } from './doc-component'; import { Liquid } from './liquid'; const PAGE_STROKE_COLOR = 'gray.200'; -const PAGE_FILL_COLOR = 'token(white)'; +const PAGE_FILL_COLOR = 'white'; const UNSPECIFIED_PAGE_FILL_COLOR = 'gray.50'; const DOCS_WORKSPACE_FILL_COLOR = 'gray.100'; const MARGIN_STROKE_COLOR = 'gray.300'; diff --git a/packages/engine-render/src/services/__tests__/canvas-color.service.spec.ts b/packages/engine-render/src/services/__tests__/canvas-color.service.spec.ts index ca1f060cd6..27812a5077 100644 --- a/packages/engine-render/src/services/__tests__/canvas-color.service.spec.ts +++ b/packages/engine-render/src/services/__tests__/canvas-color.service.spec.ts @@ -53,11 +53,9 @@ describe('CanvasColorService', () => { expect(service.getRenderColor('gray.50')).toBe('#0d1422'); expect(service.getRenderColor('white')).toBe('white'); - expect(service.getRenderColor('token(white)')).toBe('#07111f'); themeService.setTheme({ ...theme, - white: '#050914', gray: { ...theme.gray, 50: '#101827', @@ -65,7 +63,6 @@ describe('CanvasColorService', () => { }); expect(service.getRenderColor('gray.50')).toBe('#101827'); - expect(service.getRenderColor('token(white)')).toBe('#050914'); }); it('applies dark rendering to resolved design tokens', () => { @@ -164,7 +161,6 @@ describe('CanvasColorService', () => { expect(() => service.getRenderColor('alpha(gray.50, 1.1)')).toThrow('[CanvasColorService]: illegal color'); expect(() => service.getRenderColor('alpha(not-a-color, 0.5)')).toThrow('[CanvasColorService]: illegal color'); - expect(() => service.getRenderColor('token(not-a-color)')).toThrow('[CanvasColorService]: illegal color'); }); it('maps render colors for dark mode rendering', () => { diff --git a/packages/engine-render/src/services/canvas-color.service.ts b/packages/engine-render/src/services/canvas-color.service.ts index 419fbd7eb7..844365f790 100644 --- a/packages/engine-render/src/services/canvas-color.service.ts +++ b/packages/engine-render/src/services/canvas-color.service.ts @@ -71,7 +71,6 @@ const DARK_RENDER_COLOR_OVERRIDES: Record = { const COLOR_MIX_REGEXP = /^mix\(\s*([^,()]+)\s*,\s*([^,()]+)\s*,\s*(0(?:\.\d+)?|1(?:\.0+)?)\s*\)$/; const COLOR_ALPHA_REGEXP = /^alpha\(\s*([^,()]+)\s*,\s*(0(?:\.\d+)?|1(?:\.0+)?)\s*\)$/; -const THEME_TOKEN_REGEXP = /^token\(\s*([^,()]+)\s*\)$/; /** * This service inverts a color for dark mode. This service is exposed @@ -152,18 +151,6 @@ export class CanvasColorService extends Disposable implements ICanvasColorServic return cachedColor; } - const tokenMatch = inputColor.match(THEME_TOKEN_REGEXP); - if (tokenMatch) { - const token = tokenMatch[1].trim(); - const color = this._themeService.getColorFromTheme(token); - if (typeof color !== 'string' || !this._themeService.isValidThemeColor(token)) { - throw new Error(`[CanvasColorService]: illegal color "${inputColor}"`); - } - - this._resolvedColorCache.set(inputColor, color); - return color; - } - const mixMatch = inputColor.match(COLOR_MIX_REGEXP); if (mixMatch) { const color1 = this._resolveThemeColor(mixMatch[1].trim());