diff --git a/.changeset/bumpy-lizards-wear.md b/.changeset/bumpy-lizards-wear.md new file mode 100644 index 0000000000..853d812bb3 --- /dev/null +++ b/.changeset/bumpy-lizards-wear.md @@ -0,0 +1,3 @@ +--- + +--- diff --git a/.changeset/rem-unit-transform.md b/.changeset/rem-unit-transform.md new file mode 100644 index 0000000000..14b56e3889 --- /dev/null +++ b/.changeset/rem-unit-transform.md @@ -0,0 +1,21 @@ +--- +"@lynx-js/web-core": patch +--- + +feat(web-core): add support for configurable rem unit transform + +- **Description**: Added a new configuration option `transformREM` (also exposed as `transform_rem` on the Rust layer) to the Web Core renderer. When enabled, it recursively converts static `rem` unit values in your styles into dynamic CSS custom properties (`calc(VALUE * var(--rem-unit))`) during template decoding and evaluation. This enables developers to implement responsive font scaling and layout sizing dynamically on the client side simply by modifying the root CSS variable `--rem-unit`. + +- **Usage**: + You can enable this feature when working with `LynxView` by setting `transformREM` to `true`, or directly as an HTML attribute `transform-rem`: + + ```html + + ``` + + ```javascript + const lynxView = document.createElement('lynx-view'); + lynxView.transformREM = true; + ``` + + With this enabled, a CSS declaration like `font-size: 1.5rem;` is transparently evaluated as `font-size: calc(1.5 * var(--rem-unit));` by the runtime engine. diff --git a/.changeset/thin-shrimps-sip.md b/.changeset/thin-shrimps-sip.md new file mode 100644 index 0000000000..6cb2210b54 --- /dev/null +++ b/.changeset/thin-shrimps-sip.md @@ -0,0 +1,5 @@ +--- +"@lynx-js/css-serializer": patch +--- + +feat: support custom property declaration in keyframe rule diff --git a/packages/react/runtime/__test__/lifecycle.test.jsx b/packages/react/runtime/__test__/lifecycle.test.jsx index 1de785014c..29db60d394 100644 --- a/packages/react/runtime/__test__/lifecycle.test.jsx +++ b/packages/react/runtime/__test__/lifecycle.test.jsx @@ -6,7 +6,7 @@ import { globalEnvManager } from './utils/envManager'; import { waitSchedule } from './utils/nativeMethod'; import { globalCommitTaskMap, replaceCommitHook } from '../src/lifecycle/patch/commit'; import { deinitGlobalSnapshotPatch, initGlobalSnapshotPatch } from '../src/lifecycle/patch/snapshotPatch'; -import { LifecycleConstant } from '../src/lifecycleConstant'; +import { LifecycleConstant } from '../src/lifecycle/constant'; import { CATCH_ERROR } from '../src/renderToOpcodes/constants'; import { __root } from '../src/root'; import { setupPage, backgroundSnapshotInstanceManager } from '../src/snapshot'; diff --git a/packages/react/runtime/__test__/list.test.jsx b/packages/react/runtime/__test__/list.test.jsx index 3b6eba5114..70ea0303e0 100644 --- a/packages/react/runtime/__test__/list.test.jsx +++ b/packages/react/runtime/__test__/list.test.jsx @@ -2,12 +2,12 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { elementTree, nativeMethodQueue } from './utils/nativeMethod'; -import { hydrate } from '../src/hydrate'; -import { __pendingListUpdates } from '../src/pendingListUpdates'; +import { hydrate } from '../src/renderToOpcodes/hydrate'; +import { __pendingListUpdates } from '../src/list/pendingListUpdates'; import { SnapshotInstance, snapshotInstanceManager } from '../src/snapshot'; import { __root } from '../src/root'; import { globalEnvManager } from './utils/envManager'; -import { gRecycleMap, gSignMap } from '../src/list'; +import { gRecycleMap, gSignMap } from '../src/list/list'; const HOLE = null; diff --git a/packages/react/runtime/__test__/snapshot/ref.test.jsx b/packages/react/runtime/__test__/snapshot/ref.test.jsx index f5d9f88f78..b4027b873d 100644 --- a/packages/react/runtime/__test__/snapshot/ref.test.jsx +++ b/packages/react/runtime/__test__/snapshot/ref.test.jsx @@ -11,7 +11,7 @@ import { RefProxy, runDelayedUiOps, shouldDelayUiOps } from '../../src/lifecycle import { Component, createRef, useState } from '../../src/index'; import { clearCommitTaskId, replaceCommitHook } from '../../src/lifecycle/patch/commit'; import { injectUpdateMainThread } from '../../src/lifecycle/patch/updateMainThread'; -import { __pendingListUpdates } from '../../src/pendingListUpdates'; +import { __pendingListUpdates } from '../../src/list/pendingListUpdates'; import { __root } from '../../src/root'; import { setupPage } from '../../src/snapshot'; import { globalEnvManager } from '../utils/envManager'; diff --git a/packages/react/runtime/__test__/utils/envManager.ts b/packages/react/runtime/__test__/utils/envManager.ts index 54212540e9..9b6e2b84da 100644 --- a/packages/react/runtime/__test__/utils/envManager.ts +++ b/packages/react/runtime/__test__/utils/envManager.ts @@ -9,7 +9,7 @@ import { import { setupBackgroundDocument, setupDocument } from '../../src/document.js'; import { deinitGlobalSnapshotPatch } from '../../src/lifecycle/patch/snapshotPatch.js'; import { shouldDelayUiOps } from '../../src/lifecycle/ref/delay.js'; -import { clearListGlobal } from '../../src/list.js'; +import { clearListGlobal } from '../../src/list/list.js'; import { globalPipelineOptions, setPipeline } from '../../src/lynx/performance.js'; import { __root, setRoot } from '../../src/root.js'; import { SnapshotInstance, snapshotInstanceManager } from '../../src/snapshot/snapshot.js'; diff --git a/packages/react/runtime/src/debug/profile.ts b/packages/react/runtime/src/debug/profile.ts index 0fca5a70d0..232c78913d 100644 --- a/packages/react/runtime/src/debug/profile.ts +++ b/packages/react/runtime/src/debug/profile.ts @@ -2,13 +2,12 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. -/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */ +import { noop } from '../utils.js'; export const isProfiling: boolean = /* @__PURE__ */ Boolean( lynx.performance?.isProfileRecording?.(), ); -const noop = () => {}; const noopFlowId = () => 0; export const profileStart = /* @__PURE__ */ ((() => { diff --git a/packages/react/runtime/src/lifecycleConstant.ts b/packages/react/runtime/src/lifecycle/constant.ts similarity index 100% rename from packages/react/runtime/src/lifecycleConstant.ts rename to packages/react/runtime/src/lifecycle/constant.ts diff --git a/packages/react/runtime/src/lifecycle/event/delayLifecycleEvents.ts b/packages/react/runtime/src/lifecycle/event/delayLifecycleEvents.ts index 6e044d096a..7e9fc0ba85 100644 --- a/packages/react/runtime/src/lifecycle/event/delayLifecycleEvents.ts +++ b/packages/react/runtime/src/lifecycle/event/delayLifecycleEvents.ts @@ -1,7 +1,7 @@ // Copyright 2025 The Lynx Authors. All rights reserved. // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. -import type { LifecycleConstant } from '../../lifecycleConstant.js'; +import type { LifecycleConstant } from '../../lifecycle/constant.js'; const delayedLifecycleEvents: [type: LifecycleConstant, data: unknown][] = []; diff --git a/packages/react/runtime/src/lifecycle/event/jsReady.ts b/packages/react/runtime/src/lifecycle/event/jsReady.ts index ad01c39d7b..123b9ccea0 100644 --- a/packages/react/runtime/src/lifecycle/event/jsReady.ts +++ b/packages/react/runtime/src/lifecycle/event/jsReady.ts @@ -2,7 +2,7 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. import { profileEnd, profileStart } from '../../debug/profile.js'; -import { LifecycleConstant } from '../../lifecycleConstant.js'; +import { LifecycleConstant } from '../../lifecycle/constant.js'; import { __root } from '../../root.js'; let isJSReady: boolean; diff --git a/packages/react/runtime/src/lifecycle/patch/commit.ts b/packages/react/runtime/src/lifecycle/patch/commit.ts index 97d1224ed0..ffb4349e67 100644 --- a/packages/react/runtime/src/lifecycle/patch/commit.ts +++ b/packages/react/runtime/src/lifecycle/patch/commit.ts @@ -30,7 +30,7 @@ import { import { takeGlobalSnapshotPatch } from './snapshotPatch.js'; import type { SnapshotPatch } from './snapshotPatch.js'; import { profileEnd, profileStart } from '../../debug/profile.js'; -import { LifecycleConstant } from '../../lifecycleConstant.js'; +import { LifecycleConstant } from '../../lifecycle/constant.js'; import { globalPipelineOptions, markTiming, markTimingLegacy, setPipeline } from '../../lynx/performance.js'; import { COMMIT } from '../../renderToOpcodes/constants.js'; import { backgroundSnapshotInstanceManager } from '../../snapshot/backgroundSnapshot.js'; diff --git a/packages/react/runtime/src/lifecycle/patch/updateMainThread.ts b/packages/react/runtime/src/lifecycle/patch/updateMainThread.ts index 7807338232..52ec94d6ef 100644 --- a/packages/react/runtime/src/lifecycle/patch/updateMainThread.ts +++ b/packages/react/runtime/src/lifecycle/patch/updateMainThread.ts @@ -9,9 +9,9 @@ import type { PatchList, PatchOptions } from './commit.js'; import { setMainThreadHydrating } from './isMainThreadHydrating.js'; import { snapshotPatchApply } from './snapshotPatchApply.js'; import { prettyFormatSnapshotPatch } from '../../debug/formatPatch.js'; -import { LifecycleConstant } from '../../lifecycleConstant.js'; +import { LifecycleConstant } from '../../lifecycle/constant.js'; +import { __pendingListUpdates } from '../../list/pendingListUpdates.js'; import { markTiming, setPipeline } from '../../lynx/performance.js'; -import { __pendingListUpdates } from '../../pendingListUpdates.js'; import { __page } from '../../snapshot/definition.js'; import { applyRefQueue } from '../../snapshot/workletRef.js'; import { isMtsEnabled } from '../../worklet/functionality.js'; diff --git a/packages/react/runtime/src/lifecycle/reload.ts b/packages/react/runtime/src/lifecycle/reload.ts index c5c4c59141..79f4b013b7 100644 --- a/packages/react/runtime/src/lifecycle/reload.ts +++ b/packages/react/runtime/src/lifecycle/reload.ts @@ -9,21 +9,21 @@ import { render } from 'preact'; -import { hydrate } from '../hydrate.js'; -import { LifecycleConstant } from '../lifecycleConstant.js'; -import { __pendingListUpdates } from '../pendingListUpdates.js'; -import { __root, setRoot } from '../root.js'; import { destroyBackground } from './destroy.js'; +import { increaseReloadVersion } from './pass.js'; +import { renderMainThread } from './render.js'; +import { profileEnd, profileStart } from '../debug/profile.js'; +import { LifecycleConstant } from '../lifecycle/constant.js'; +import { __pendingListUpdates } from '../list/pendingListUpdates.js'; +import { hydrate } from '../renderToOpcodes/hydrate.js'; +import { __root, setRoot } from '../root.js'; +import { __page } from '../snapshot/definition.js'; import { SnapshotInstance, snapshotInstanceManager } from '../snapshot/snapshot.js'; import { applyRefQueue } from '../snapshot/workletRef.js'; import { isEmptyObject } from '../utils.js'; import { clearJSReadyEventIdSwap, isJSReady } from './event/jsReady.js'; -import { increaseReloadVersion } from './pass.js'; import { deinitGlobalSnapshotPatch } from './patch/snapshotPatch.js'; import { shouldDelayUiOps } from './ref/delay.js'; -import { renderMainThread } from './render.js'; -import { profileEnd, profileStart } from '../debug/profile.js'; -import { __page } from '../snapshot/definition.js'; function reloadMainThread(data: unknown, options: UpdatePageOption): void { if (typeof __PROFILE__ !== 'undefined' && __PROFILE__) { diff --git a/packages/react/runtime/src/list.ts b/packages/react/runtime/src/list/list.ts similarity index 97% rename from packages/react/runtime/src/list.ts rename to packages/react/runtime/src/list/list.ts index 736303b2a8..7569ae4270 100644 --- a/packages/react/runtime/src/list.ts +++ b/packages/react/runtime/src/list/list.ts @@ -1,10 +1,10 @@ // Copyright 2024 The Lynx Authors. All rights reserved. // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. -import { LifecycleConstant } from './lifecycleConstant.js'; -import type { SnapshotInstance } from './snapshot/snapshot.js'; -import { applyRefQueue } from './snapshot/workletRef.js'; -import { maybePromise } from './utils.js'; +import { LifecycleConstant } from '../lifecycle/constant.js'; +import type { SnapshotInstance } from '../snapshot/snapshot.js'; +import { applyRefQueue } from '../snapshot/workletRef.js'; +import { maybePromise } from '../utils.js'; export const gSignMap: Record> = {}; export const gRecycleMap: Record>> = {}; diff --git a/packages/react/runtime/src/listUpdateInfo.ts b/packages/react/runtime/src/list/listUpdateInfo.ts similarity index 97% rename from packages/react/runtime/src/listUpdateInfo.ts rename to packages/react/runtime/src/list/listUpdateInfo.ts index 6c8db2c1ad..c53bc69bd4 100644 --- a/packages/react/runtime/src/listUpdateInfo.ts +++ b/packages/react/runtime/src/list/listUpdateInfo.ts @@ -1,11 +1,10 @@ // Copyright 2025 The Lynx Authors. All rights reserved. // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. - -import { profileEnd, profileStart } from './debug/profile.js'; -import { hydrate } from './hydrate.js'; import { componentAtIndexFactory, enqueueComponentFactory } from './list.js'; -import type { SnapshotInstance } from './snapshot/snapshot.js'; +import { profileEnd, profileStart } from '../debug/profile.js'; +import { hydrate } from '../renderToOpcodes/hydrate.js'; +import type { SnapshotInstance } from '../snapshot/snapshot.js'; export interface ListUpdateInfo { flush(): number | undefined; diff --git a/packages/react/runtime/src/pendingListUpdates.ts b/packages/react/runtime/src/list/pendingListUpdates.ts similarity index 100% rename from packages/react/runtime/src/pendingListUpdates.ts rename to packages/react/runtime/src/list/pendingListUpdates.ts diff --git a/packages/react/runtime/src/lynx-api.ts b/packages/react/runtime/src/lynx-api.ts index e975250087..410c168171 100644 --- a/packages/react/runtime/src/lynx-api.ts +++ b/packages/react/runtime/src/lynx-api.ts @@ -9,7 +9,7 @@ import type { Consumer, FC, ReactNode } from 'react'; import { factory, withInitDataInState } from './compat/initData.js'; import { profileEnd, profileStart } from './debug/profile.js'; import { useLynxGlobalEventListener } from './hooks/useLynxGlobalEventListener.js'; -import { LifecycleConstant } from './lifecycleConstant.js'; +import { LifecycleConstant } from './lifecycle/constant.js'; import { flushDelayedLifecycleEvents } from './lynx/tt.js'; import { __root } from './root.js'; diff --git a/packages/react/runtime/src/lynx/calledByNative.ts b/packages/react/runtime/src/lynx/calledByNative.ts index cccd0bf927..7b397412ff 100644 --- a/packages/react/runtime/src/lynx/calledByNative.ts +++ b/packages/react/runtime/src/lynx/calledByNative.ts @@ -1,15 +1,15 @@ // Copyright 2024 The Lynx Authors. All rights reserved. // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. -import { hydrate } from '../hydrate.js'; +import { markTiming, setPipeline } from './performance.js'; +import { LifecycleConstant } from '../lifecycle/constant.js'; import { isJSReady, jsReady, jsReadyEventIdSwap, resetJSReady } from '../lifecycle/event/jsReady.js'; import { reloadMainThread } from '../lifecycle/reload.js'; import { renderMainThread } from '../lifecycle/render.js'; -import { LifecycleConstant } from '../lifecycleConstant.js'; -import { ssrHydrateByOpcodes } from '../opcodes.js'; -import { __pendingListUpdates } from '../pendingListUpdates.js'; +import { __pendingListUpdates } from '../list/pendingListUpdates.js'; +import { hydrate } from '../renderToOpcodes/hydrate.js'; +import { ssrHydrateByOpcodes } from '../renderToOpcodes/opcodes.js'; import { __root, setRoot } from '../root.js'; -import { markTiming, setPipeline } from './performance.js'; import { __page, setupPage } from '../snapshot/definition.js'; import { SnapshotInstance } from '../snapshot/snapshot.js'; import { applyRefQueue } from '../snapshot/workletRef.js'; diff --git a/packages/react/runtime/src/lynx/tt.ts b/packages/react/runtime/src/lynx/tt.ts index 635dc0a664..55552c182e 100644 --- a/packages/react/runtime/src/lynx/tt.ts +++ b/packages/react/runtime/src/lynx/tt.ts @@ -3,13 +3,13 @@ // LICENSE file in the root directory of this source tree. import { process, render } from 'preact'; -import { LifecycleConstant, NativeUpdateDataType } from '../lifecycleConstant.js'; -import type { FirstScreenData } from '../lifecycleConstant.js'; import { PerformanceTimingFlags, PipelineOrigins, beginPipeline, markTiming } from './performance.js'; import { runWithForce } from './runWithForce.js'; import { printSnapshotInstanceToString } from '../debug/printSnapshot.js'; import { profileEnd, profileStart } from '../debug/profile.js'; import { getSnapshotVNodeSource } from '../debug/vnodeSource.js'; +import { LifecycleConstant, NativeUpdateDataType } from '../lifecycle/constant.js'; +import type { FirstScreenData } from '../lifecycle/constant.js'; import { destroyBackground } from '../lifecycle/destroy.js'; import { delayedEvents, delayedPublishEvent } from '../lifecycle/event/delayEvents.js'; import { delayLifecycleEvent, delayedLifecycleEvents } from '../lifecycle/event/delayLifecycleEvents.js'; diff --git a/packages/react/runtime/src/hydrate.ts b/packages/react/runtime/src/renderToOpcodes/hydrate.ts similarity index 96% rename from packages/react/runtime/src/hydrate.ts rename to packages/react/runtime/src/renderToOpcodes/hydrate.ts index 69b00fbfb0..cee54eaabe 100644 --- a/packages/react/runtime/src/hydrate.ts +++ b/packages/react/runtime/src/renderToOpcodes/hydrate.ts @@ -2,14 +2,14 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. -import { profileEnd, profileStart } from './debug/profile.js'; -import { componentAtIndexFactory, enqueueComponentFactory, gRecycleMap, gSignMap } from './list.js'; -import { __pendingListUpdates } from './pendingListUpdates.js'; -import { DynamicPartType } from './snapshot/dynamicPartType.js'; -import type { PlatformInfo } from './snapshot/platformInfo.js'; -import { unref } from './snapshot/ref.js'; -import type { SnapshotInstance } from './snapshot/snapshot.js'; -import { isEmptyObject } from './utils.js'; +import { profileEnd, profileStart } from '../debug/profile.js'; +import { componentAtIndexFactory, enqueueComponentFactory, gRecycleMap, gSignMap } from '../list/list.js'; +import { __pendingListUpdates } from '../list/pendingListUpdates.js'; +import { DynamicPartType } from '../snapshot/dynamicPartType.js'; +import type { PlatformInfo } from '../snapshot/platformInfo.js'; +import { unref } from '../snapshot/ref.js'; +import type { SnapshotInstance } from '../snapshot/snapshot.js'; +import { isEmptyObject } from '../utils.js'; const UNREACHABLE_ITEM_KEY_NOT_FOUND = 'UNREACHABLE_ITEM_KEY_NOT_FOUND'; diff --git a/packages/react/runtime/src/opcodes.ts b/packages/react/runtime/src/renderToOpcodes/opcodes.ts similarity index 96% rename from packages/react/runtime/src/opcodes.ts rename to packages/react/runtime/src/renderToOpcodes/opcodes.ts index 881bc6548a..365e2fc191 100644 --- a/packages/react/runtime/src/opcodes.ts +++ b/packages/react/runtime/src/renderToOpcodes/opcodes.ts @@ -2,9 +2,9 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. import { hydrate } from './hydrate.js'; -import { componentAtIndexFactory, enqueueComponentFactory, gRecycleMap, gSignMap } from './list.js'; -import { CHILDREN } from './renderToOpcodes/constants.js'; -import { SnapshotInstance } from './snapshot/snapshot.js'; +import { componentAtIndexFactory, enqueueComponentFactory, gRecycleMap, gSignMap } from '../list/list.js'; +import { CHILDREN } from './constants.js'; +import { SnapshotInstance } from '../snapshot/snapshot.js'; const OpcodeBegin = 0; const OpcodeEnd = 1; diff --git a/packages/react/runtime/src/snapshot/backgroundSnapshot.ts b/packages/react/runtime/src/snapshot/backgroundSnapshot.ts index b2b610c5f9..18c56ed0f2 100644 --- a/packages/react/runtime/src/snapshot/backgroundSnapshot.ts +++ b/packages/react/runtime/src/snapshot/backgroundSnapshot.ts @@ -10,11 +10,6 @@ import type { Worklet } from '@lynx-js/react/worklet-runtime/bindings'; -import { profileEnd, profileStart } from '../debug/profile.js'; -import { clearSnapshotVNodeSource, getSnapshotVNodeSource, moveSnapshotVNodeSource } from '../debug/vnodeSource.js'; -import { processGestureBackground } from '../gesture/processGestureBagkround.js'; -import type { GestureKind } from '../gesture/types.js'; -import { diffArrayAction, diffArrayLepus } from '../hydrate.js'; import { snapshotManager } from './definition.js'; import type { Snapshot } from './definition.js'; import { DynamicPartType } from './dynamicPartType.js'; @@ -25,6 +20,10 @@ import { hydrationMap } from './snapshotInstanceHydrationMap.js'; import { transformSpread } from './spread.js'; import type { SerializedSnapshotInstance } from './types.js'; import { traverseSnapshotInstance } from './utils.js'; +import { profileEnd, profileStart } from '../debug/profile.js'; +import { clearSnapshotVNodeSource, getSnapshotVNodeSource, moveSnapshotVNodeSource } from '../debug/vnodeSource.js'; +import { processGestureBackground } from '../gesture/processGestureBagkround.js'; +import type { GestureKind } from '../gesture/types.js'; import { globalBackgroundSnapshotInstancesToRemove } from '../lifecycle/patch/globalState.js'; import { SnapshotOperation, @@ -34,6 +33,7 @@ import { } from '../lifecycle/patch/snapshotPatch.js'; import type { SnapshotPatch } from '../lifecycle/patch/snapshotPatch.js'; import { globalPipelineOptions } from '../lynx/performance.js'; +import { diffArrayAction, diffArrayLepus } from '../renderToOpcodes/hydrate.js'; import { isDirectOrDeepEqual } from '../utils.js'; import { onPostWorkletCtx } from '../worklet/ctx.js'; diff --git a/packages/react/runtime/src/snapshot/list.ts b/packages/react/runtime/src/snapshot/list.ts index 972433e888..426f80b2e9 100644 --- a/packages/react/runtime/src/snapshot/list.ts +++ b/packages/react/runtime/src/snapshot/list.ts @@ -2,8 +2,8 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. -import { hydrate } from '../hydrate.js'; -import { componentAtIndexFactory, enqueueComponentFactory, gRecycleMap, gSignMap } from '../list.js'; +import { componentAtIndexFactory, enqueueComponentFactory, gRecycleMap, gSignMap } from '../list/list.js'; +import { hydrate } from '../renderToOpcodes/hydrate.js'; import type { SnapshotInstance } from '../snapshot/snapshot.js'; const destroyLifetimeHandlerMap = new Map void>(); diff --git a/packages/react/runtime/src/snapshot/platformInfo.ts b/packages/react/runtime/src/snapshot/platformInfo.ts index 4612033141..70f88a57b0 100644 --- a/packages/react/runtime/src/snapshot/platformInfo.ts +++ b/packages/react/runtime/src/snapshot/platformInfo.ts @@ -1,8 +1,8 @@ // Copyright 2025 The Lynx Authors. All rights reserved. // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. -import { ListUpdateInfoRecording } from '../listUpdateInfo.js'; -import { __pendingListUpdates } from '../pendingListUpdates.js'; +import { ListUpdateInfoRecording } from '../list/listUpdateInfo.js'; +import { __pendingListUpdates } from '../list/pendingListUpdates.js'; import type { SnapshotInstance } from '../snapshot/snapshot.js'; const platformInfoVirtualAttributes: Set = /* @__PURE__ */ new Set([ diff --git a/packages/react/runtime/src/snapshot/snapshot.ts b/packages/react/runtime/src/snapshot/snapshot.ts index 1464690583..e5b9722a64 100644 --- a/packages/react/runtime/src/snapshot/snapshot.ts +++ b/packages/react/runtime/src/snapshot/snapshot.ts @@ -10,10 +10,6 @@ import type { Worklet, WorkletRefImpl } from '@lynx-js/react/worklet-runtime/bindings'; -import { clearSnapshotVNodeSource } from '../debug/vnodeSource.js'; -import { SnapshotOperation, __globalSnapshotPatch } from '../lifecycle/patch/snapshotPatch.js'; -import { ListUpdateInfoRecording } from '../listUpdateInfo.js'; -import { __pendingListUpdates } from '../pendingListUpdates.js'; import { DEFAULT_CSS_ID, DEFAULT_ENTRY_NAME } from './constants.js'; import { snapshotManager } from './definition.js'; import type { Snapshot } from './definition.js'; @@ -23,6 +19,10 @@ import type { PlatformInfo } from './platformInfo.js'; import { unref } from './ref.js'; import type { SerializedSnapshotInstance } from './types.js'; import { traverseSnapshotInstance } from './utils.js'; +import { clearSnapshotVNodeSource } from '../debug/vnodeSource.js'; +import { SnapshotOperation, __globalSnapshotPatch } from '../lifecycle/patch/snapshotPatch.js'; +import { ListUpdateInfoRecording } from '../list/listUpdateInfo.js'; +import { __pendingListUpdates } from '../list/pendingListUpdates.js'; import { isDirectOrDeepEqual } from '../utils.js'; export const snapshotInstanceManager: { diff --git a/packages/react/runtime/src/snapshot/spread.ts b/packages/react/runtime/src/snapshot/spread.ts index 14e8edb3fd..a68ffc45b9 100644 --- a/packages/react/runtime/src/snapshot/spread.ts +++ b/packages/react/runtime/src/snapshot/spread.ts @@ -12,8 +12,8 @@ import type { Element, Worklet, WorkletRefImpl } from '@lynx-js/react/worklet-runtime/bindings'; import type { BackgroundSnapshotInstance } from './backgroundSnapshot.js'; -import { ListUpdateInfoRecording } from '../listUpdateInfo.js'; -import { __pendingListUpdates } from '../pendingListUpdates.js'; +import { ListUpdateInfoRecording } from '../list/listUpdateInfo.js'; +import { __pendingListUpdates } from '../list/pendingListUpdates.js'; import { SnapshotInstance } from '../snapshot/snapshot.js'; import { isDirectOrDeepEqual, isEmptyObject, pick } from '../utils.js'; import { updateEvent } from './event.js'; diff --git a/packages/react/runtime/src/worklet/ref/updateInitValue.ts b/packages/react/runtime/src/worklet/ref/updateInitValue.ts index f547b3228d..379b4caf67 100644 --- a/packages/react/runtime/src/worklet/ref/updateInitValue.ts +++ b/packages/react/runtime/src/worklet/ref/updateInitValue.ts @@ -6,7 +6,7 @@ import { updateWorkletRefInitValueChanges } from '@lynx-js/react/worklet-runtime import { takeWorkletRefInitValuePatch } from './workletRefPool.js'; import type { workletRefInitValuePatch } from './workletRefPool.js'; -import { LifecycleConstant } from '../../lifecycleConstant.js'; +import { LifecycleConstant } from '../../lifecycle/constant.js'; function updateMTRefInitValue({ data }: { data: string }): void { // This update ignores reloadVersion check. diff --git a/packages/react/runtime/types/types.d.ts b/packages/react/runtime/types/types.d.ts index c3a8333e31..c69a0ed1c2 100644 --- a/packages/react/runtime/types/types.d.ts +++ b/packages/react/runtime/types/types.d.ts @@ -4,7 +4,7 @@ import { EventEmitter } from '@lynx-js/types'; -import { LifecycleConstant } from '../src/lifecycleConstant.js'; +import { LifecycleConstant } from '../src/lifecycle/constant.js'; import { Lynx as LynxApi } from '../src/lynx-api.js'; import type { InitData, InitDataRaw } from '../src/lynx-api.js'; diff --git a/packages/react/testing-library/src/__tests__/list.test.jsx b/packages/react/testing-library/src/__tests__/list.test.jsx index 775a16cab7..cb718f18ef 100644 --- a/packages/react/testing-library/src/__tests__/list.test.jsx +++ b/packages/react/testing-library/src/__tests__/list.test.jsx @@ -8,7 +8,7 @@ import { describe, expect } from 'vitest'; import { Component, useState } from '@lynx-js/react'; import { render } from '..'; -import { __pendingListUpdates } from '../../../runtime/lib/pendingListUpdates.js'; +import { __pendingListUpdates } from '../../../runtime/lib/list/pendingListUpdates.js'; describe('list', () => { it('basic', async () => { diff --git a/packages/tools/css-serializer/src/parse.ts b/packages/tools/css-serializer/src/parse.ts index 27844d3c72..56852d4431 100644 --- a/packages/tools/css-serializer/src/parse.ts +++ b/packages/tools/css-serializer/src/parse.ts @@ -117,6 +117,21 @@ function transformDeclaration( } } +function transformVariables(block: csstree.Block): Record { + return Object.fromEntries( + block.children.toArray().filter((node) => { + return node.type === 'Declaration' && node.property.startsWith('--'); + }).map((node) => { + const declaration = node as csstree.Declaration; + return [ + declaration.property, + toString(declaration.value) + + (declaration.important ? ' !important' : ''), + ]; + }), + ); +} + function transformStyleRule( node: csstree.Rule, errors: ParserError[], @@ -129,19 +144,7 @@ function transformStyleRule( value: preludeText, loc: toLoc(node.prelude.loc!.end), }, - variables: Object.fromEntries( - node.block.children.toArray().filter(node => - node.type === 'Declaration' && node.property.startsWith('--') - ).map((node) => { - return [ - (node as csstree.Declaration).property, - toString((node as csstree.Declaration).value) - + ((node as csstree.Declaration).important - ? ' !important' - : ''), - ]; - }), - ), + variables: transformVariables(node.block), }; } @@ -337,6 +340,7 @@ export function parse(content: string, options: { value: preludeText, loc: toLoc(rule.prelude.loc!.start, preludeText.length), }, + variables: transformVariables(rule.block), style: transformBlock(rule.block, errors), }; }), diff --git a/packages/tools/css-serializer/src/types/LynxStyleNode.ts b/packages/tools/css-serializer/src/types/LynxStyleNode.ts index 9100b858f0..bdc562f70b 100644 --- a/packages/tools/css-serializer/src/types/LynxStyleNode.ts +++ b/packages/tools/css-serializer/src/types/LynxStyleNode.ts @@ -45,6 +45,7 @@ export interface ImportRule { } export interface KeyframesStyle { keyText: Value; + variables: Record; style: Declaration[]; } export interface KeyframesRule { diff --git a/packages/tools/css-serializer/test/__preparation__/keyframes.css b/packages/tools/css-serializer/test/__preparation__/keyframes.css index 8ff873e252..7ebaf554e9 100644 --- a/packages/tools/css-serializer/test/__preparation__/keyframes.css +++ b/packages/tools/css-serializer/test/__preparation__/keyframes.css @@ -33,3 +33,20 @@ c: none; } } + +@keyframes test-keyframe-declaration { + 0% { + --bg-color: red; + background-color: var(--bg-color); + } + + 50% { + --bg-color: green; + background-color: var(--bg-color); + } + + 100% { + --bg-color: blue; + background-color: var(--bg-color); + } +} diff --git a/packages/tools/css-serializer/test/__snapshots__/lynx.spec.ts.snap b/packages/tools/css-serializer/test/__snapshots__/lynx.spec.ts.snap index 79f3089118..14ee2f67d1 100644 --- a/packages/tools/css-serializer/test/__snapshots__/lynx.spec.ts.snap +++ b/packages/tools/css-serializer/test/__snapshots__/lynx.spec.ts.snap @@ -2030,6 +2030,7 @@ exports[`keyframes 1`] = ` "value": "translateX(0%)", }, ], + "variables": {}, }, { "keyText": { @@ -2053,6 +2054,7 @@ exports[`keyframes 1`] = ` "value": "translateX(100%)", }, ], + "variables": {}, }, ], "type": "KeyframesRule", @@ -2100,6 +2102,7 @@ exports[`keyframes 1`] = ` "value": "0", }, ], + "variables": {}, }, { "keyText": { @@ -2123,6 +2126,7 @@ exports[`keyframes 1`] = ` "value": "50px", }, ], + "variables": {}, }, { "keyText": { @@ -2146,6 +2150,7 @@ exports[`keyframes 1`] = ` "value": "50px", }, ], + "variables": {}, }, { "keyText": { @@ -2181,6 +2186,7 @@ exports[`keyframes 1`] = ` "value": "100%", }, ], + "variables": {}, }, ], "type": "KeyframesRule", @@ -2216,6 +2222,7 @@ exports[`keyframes 1`] = ` "value": "1", }, ], + "variables": {}, }, { "keyText": { @@ -2251,6 +2258,7 @@ exports[`keyframes 1`] = ` "value": "3", }, ], + "variables": {}, }, { "keyText": { @@ -2274,6 +2282,7 @@ exports[`keyframes 1`] = ` "value": "5", }, ], + "variables": {}, }, { "keyText": { @@ -2297,6 +2306,112 @@ exports[`keyframes 1`] = ` "value": "none", }, ], + "variables": {}, + }, + ], + "type": "KeyframesRule", + }, + { + "name": { + "loc": { + "column": 37, + "line": 37, + }, + "value": "test-keyframe-declaration", + }, + "styles": [ + { + "keyText": { + "loc": { + "column": 5, + "line": 38, + }, + "value": "0%", + }, + "style": [ + { + "defaultValue": "", + "defaultValueMap": { + "--bg-color": "", + }, + "keyLoc": { + "column": 21, + "line": 40, + }, + "name": "background-color", + "type": "css_var", + "valLoc": { + "column": 39, + "line": 40, + }, + "value": "{{--bg-color}}", + }, + ], + "variables": { + "--bg-color": "red", + }, + }, + { + "keyText": { + "loc": { + "column": 6, + "line": 43, + }, + "value": "50%", + }, + "style": [ + { + "defaultValue": "", + "defaultValueMap": { + "--bg-color": "", + }, + "keyLoc": { + "column": 21, + "line": 45, + }, + "name": "background-color", + "type": "css_var", + "valLoc": { + "column": 39, + "line": 45, + }, + "value": "{{--bg-color}}", + }, + ], + "variables": { + "--bg-color": "green", + }, + }, + { + "keyText": { + "loc": { + "column": 7, + "line": 48, + }, + "value": "100%", + }, + "style": [ + { + "defaultValue": "", + "defaultValueMap": { + "--bg-color": "", + }, + "keyLoc": { + "column": 21, + "line": 50, + }, + "name": "background-color", + "type": "css_var", + "valLoc": { + "column": 39, + "line": 50, + }, + "value": "{{--bg-color}}", + }, + ], + "variables": { + "--bg-color": "blue", + }, }, ], "type": "KeyframesRule", diff --git a/packages/web-platform/web-core/binary/client/client.d.ts b/packages/web-platform/web-core/binary/client/client.d.ts index 525ea62e99..2c21820086 100644 --- a/packages/web-platform/web-core/binary/client/client.d.ts +++ b/packages/web-platform/web-core/binary/client/client.d.ts @@ -160,17 +160,17 @@ export class StyleSheetResource { */ export function add_inline_style_raw_string_key(dom: HTMLElement, key: string, value?: string | null): void; -export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; -export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; export function get_font_face_content(buffer: Uint8Array): string; export function get_style_content(buffer: Uint8Array): string; -export function set_inline_styles_in_key_value_vec(dom: HTMLElement, k_v_vec: string[], transform_vw: boolean, transform_vh: boolean): void; +export function set_inline_styles_in_key_value_vec(dom: HTMLElement, k_v_vec: string[], transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): void; -export function set_inline_styles_in_str(dom: HTMLElement, styles: string, transform_vw: boolean, transform_vh: boolean): boolean; +export function set_inline_styles_in_str(dom: HTMLElement, styles: string, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): boolean; export function set_inline_styles_number_key(dom: HTMLElement, key: number, value?: string | null): void; @@ -192,8 +192,8 @@ export interface InitOutput { readonly __wbg_set_eventinfo_event_type: (a: number, b: number, c: number) => void; readonly __wbg_stylesheetresource_free: (a: number, b: number) => void; readonly add_inline_style_raw_string_key: (a: any, b: number, c: number, d: number, e: number) => void; - readonly decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; - readonly encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; + readonly decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; + readonly encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; readonly get_font_face_content: (a: any) => [number, number, number, number]; readonly get_style_content: (a: any) => [number, number, number, number]; readonly mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; @@ -231,8 +231,8 @@ export interface InitOutput { readonly ruleprelude_new: () => number; readonly ruleprelude_push_selector: (a: number, b: number) => void; readonly selector_push_one_selector_section: (a: number, b: number, c: number, d: number, e: number) => [number, number]; - readonly set_inline_styles_in_key_value_vec: (a: any, b: number, c: number, d: number, e: number) => void; - readonly set_inline_styles_in_str: (a: any, b: number, c: number, d: number, e: number) => number; + readonly set_inline_styles_in_key_value_vec: (a: any, b: number, c: number, d: number, e: number, f: number) => void; + readonly set_inline_styles_in_str: (a: any, b: number, c: number, d: number, e: number, f: number) => number; readonly set_inline_styles_number_key: (a: any, b: number, c: number, d: number) => void; readonly stylesheetresource_new: (a: any, b: any) => [number, number, number]; readonly selector_new: () => number; diff --git a/packages/web-platform/web-core/binary/client/client_bg.wasm.d.ts b/packages/web-platform/web-core/binary/client/client_bg.wasm.d.ts index 28ee68a718..6e315ce555 100644 --- a/packages/web-platform/web-core/binary/client/client_bg.wasm.d.ts +++ b/packages/web-platform/web-core/binary/client/client_bg.wasm.d.ts @@ -15,8 +15,8 @@ export const __wbg_set_eventinfo_event_name: (a: number, b: number, c: number) = export const __wbg_set_eventinfo_event_type: (a: number, b: number, c: number) => void; export const __wbg_stylesheetresource_free: (a: number, b: number) => void; export const add_inline_style_raw_string_key: (a: any, b: number, c: number, d: number, e: number) => void; -export const decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; -export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; +export const decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; +export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; export const get_font_face_content: (a: any) => [number, number, number, number]; export const get_style_content: (a: any) => [number, number, number, number]; export const mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; @@ -54,8 +54,8 @@ export const rule_set_prelude: (a: number, b: number) => void; export const ruleprelude_new: () => number; export const ruleprelude_push_selector: (a: number, b: number) => void; export const selector_push_one_selector_section: (a: number, b: number, c: number, d: number, e: number) => [number, number]; -export const set_inline_styles_in_key_value_vec: (a: any, b: number, c: number, d: number, e: number) => void; -export const set_inline_styles_in_str: (a: any, b: number, c: number, d: number, e: number) => number; +export const set_inline_styles_in_key_value_vec: (a: any, b: number, c: number, d: number, e: number, f: number) => void; +export const set_inline_styles_in_str: (a: any, b: number, c: number, d: number, e: number, f: number) => number; export const set_inline_styles_number_key: (a: any, b: number, c: number, d: number) => void; export const stylesheetresource_new: (a: any, b: any) => [number, number, number]; export const selector_new: () => number; diff --git a/packages/web-platform/web-core/binary/client_legacy/client.d.ts b/packages/web-platform/web-core/binary/client_legacy/client.d.ts index cb4310ddf3..c07a8fbd63 100644 --- a/packages/web-platform/web-core/binary/client_legacy/client.d.ts +++ b/packages/web-platform/web-core/binary/client_legacy/client.d.ts @@ -160,17 +160,17 @@ export class StyleSheetResource { */ export function add_inline_style_raw_string_key(dom: HTMLElement, key: string, value?: string | null): void; -export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; -export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; export function get_font_face_content(buffer: Uint8Array): string; export function get_style_content(buffer: Uint8Array): string; -export function set_inline_styles_in_key_value_vec(dom: HTMLElement, k_v_vec: string[], transform_vw: boolean, transform_vh: boolean): void; +export function set_inline_styles_in_key_value_vec(dom: HTMLElement, k_v_vec: string[], transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): void; -export function set_inline_styles_in_str(dom: HTMLElement, styles: string, transform_vw: boolean, transform_vh: boolean): boolean; +export function set_inline_styles_in_str(dom: HTMLElement, styles: string, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): boolean; export function set_inline_styles_number_key(dom: HTMLElement, key: number, value?: string | null): void; @@ -192,8 +192,8 @@ export interface InitOutput { readonly __wbg_set_eventinfo_event_type: (a: number, b: number, c: number) => void; readonly __wbg_stylesheetresource_free: (a: number, b: number) => void; readonly add_inline_style_raw_string_key: (a: number, b: number, c: number, d: number, e: number) => void; - readonly decode_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; - readonly encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; + readonly decode_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; + readonly encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; readonly get_font_face_content: (a: number, b: number) => void; readonly get_style_content: (a: number, b: number) => void; readonly mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; @@ -231,8 +231,8 @@ export interface InitOutput { readonly ruleprelude_new: () => number; readonly ruleprelude_push_selector: (a: number, b: number) => void; readonly selector_push_one_selector_section: (a: number, b: number, c: number, d: number, e: number, f: number) => void; - readonly set_inline_styles_in_key_value_vec: (a: number, b: number, c: number, d: number, e: number) => void; - readonly set_inline_styles_in_str: (a: number, b: number, c: number, d: number, e: number) => number; + readonly set_inline_styles_in_key_value_vec: (a: number, b: number, c: number, d: number, e: number, f: number) => void; + readonly set_inline_styles_in_str: (a: number, b: number, c: number, d: number, e: number, f: number) => number; readonly set_inline_styles_number_key: (a: number, b: number, c: number, d: number) => void; readonly stylesheetresource_new: (a: number, b: number, c: number) => void; readonly selector_new: () => number; diff --git a/packages/web-platform/web-core/binary/client_legacy/client_bg.wasm.d.ts b/packages/web-platform/web-core/binary/client_legacy/client_bg.wasm.d.ts index 75ae3b0b4a..51fabb5823 100644 --- a/packages/web-platform/web-core/binary/client_legacy/client_bg.wasm.d.ts +++ b/packages/web-platform/web-core/binary/client_legacy/client_bg.wasm.d.ts @@ -15,8 +15,8 @@ export const __wbg_set_eventinfo_event_name: (a: number, b: number, c: number) = export const __wbg_set_eventinfo_event_type: (a: number, b: number, c: number) => void; export const __wbg_stylesheetresource_free: (a: number, b: number) => void; export const add_inline_style_raw_string_key: (a: number, b: number, c: number, d: number, e: number) => void; -export const decode_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; -export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; +export const decode_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; +export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; export const get_font_face_content: (a: number, b: number) => void; export const get_style_content: (a: number, b: number) => void; export const mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; @@ -54,8 +54,8 @@ export const rule_set_prelude: (a: number, b: number) => void; export const ruleprelude_new: () => number; export const ruleprelude_push_selector: (a: number, b: number) => void; export const selector_push_one_selector_section: (a: number, b: number, c: number, d: number, e: number, f: number) => void; -export const set_inline_styles_in_key_value_vec: (a: number, b: number, c: number, d: number, e: number) => void; -export const set_inline_styles_in_str: (a: number, b: number, c: number, d: number, e: number) => number; +export const set_inline_styles_in_key_value_vec: (a: number, b: number, c: number, d: number, e: number, f: number) => void; +export const set_inline_styles_in_str: (a: number, b: number, c: number, d: number, e: number, f: number) => number; export const set_inline_styles_number_key: (a: number, b: number, c: number, d: number) => void; export const stylesheetresource_new: (a: number, b: number, c: number) => void; export const selector_new: () => number; diff --git a/packages/web-platform/web-core/binary/encode/encode.d.ts b/packages/web-platform/web-core/binary/encode/encode.d.ts index 6844850a6a..a4534787c6 100644 --- a/packages/web-platform/web-core/binary/encode/encode.d.ts +++ b/packages/web-platform/web-core/binary/encode/encode.d.ts @@ -109,9 +109,9 @@ export class StyleInfoDecoder { [Symbol.dispose](): void; } -export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; -export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; export function get_font_face_content(buffer: Uint8Array): string; diff --git a/packages/web-platform/web-core/binary/encode/encode_bg.wasm.d.ts b/packages/web-platform/web-core/binary/encode/encode_bg.wasm.d.ts index b6a0949cde..6d26873978 100644 --- a/packages/web-platform/web-core/binary/encode/encode_bg.wasm.d.ts +++ b/packages/web-platform/web-core/binary/encode/encode_bg.wasm.d.ts @@ -6,8 +6,8 @@ export const __wbg_rule_free: (a: number, b: number) => void; export const __wbg_ruleprelude_free: (a: number, b: number) => void; export const __wbg_selector_free: (a: number, b: number) => void; export const __wbg_styleinfodecoder_free: (a: number, b: number) => void; -export const decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; -export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; +export const decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; +export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; export const get_font_face_content: (a: any) => [number, number, number, number]; export const get_style_content: (a: any) => [number, number, number, number]; export const rawstyleinfo_append_import: (a: number, b: number, c: number) => void; diff --git a/packages/web-platform/web-core/binary/server/server.d.ts b/packages/web-platform/web-core/binary/server/server.d.ts index 7c45e78d63..cf5898de3c 100644 --- a/packages/web-platform/web-core/binary/server/server.d.ts +++ b/packages/web-platform/web-core/binary/server/server.d.ts @@ -15,7 +15,7 @@ export class MainThreadServerContext { get_parent(child_id: number): number | undefined; get_tag(element_id: number): string | undefined; insert_before(parent_id: number, child_id: number, ref_id?: number | null): void; - constructor(view_attributes: string, enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean); + constructor(view_attributes: string, enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean); push_style_sheet(resource: StyleSheetResource, entry_name?: string | null): void; remove_attribute(element_id: number, key: string): void; remove_child(parent_id: number, child_id: number): void; @@ -130,9 +130,9 @@ export class StyleSheetResource { constructor(buffer: Uint8Array, _document: any); } -export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; -export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; export function get_font_face_content(buffer: Uint8Array): string; diff --git a/packages/web-platform/web-core/binary/server/server_bg.wasm.d.ts b/packages/web-platform/web-core/binary/server/server_bg.wasm.d.ts index e0f2ca4a39..f334a6c1af 100644 --- a/packages/web-platform/web-core/binary/server/server_bg.wasm.d.ts +++ b/packages/web-platform/web-core/binary/server/server_bg.wasm.d.ts @@ -7,8 +7,8 @@ export const __wbg_rule_free: (a: number, b: number) => void; export const __wbg_ruleprelude_free: (a: number, b: number) => void; export const __wbg_selector_free: (a: number, b: number) => void; export const __wbg_stylesheetresource_free: (a: number, b: number) => void; -export const decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; -export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; +export const decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; +export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; export const get_font_face_content: (a: any) => [number, number, number, number]; export const get_style_content: (a: any) => [number, number, number, number]; export const init_server_in_shadow_css: (a: number, b: number) => void; @@ -23,7 +23,7 @@ export const mainthreadservercontext_get_page_css: (a: number) => [number, numbe export const mainthreadservercontext_get_parent: (a: number, b: number) => number; export const mainthreadservercontext_get_tag: (a: number, b: number) => [number, number]; export const mainthreadservercontext_insert_before: (a: number, b: number, c: number, d: number) => void; -export const mainthreadservercontext_new: (a: number, b: number, c: number, d: number, e: number) => number; +export const mainthreadservercontext_new: (a: number, b: number, c: number, d: number, e: number, f: number) => number; export const mainthreadservercontext_push_style_sheet: (a: number, b: number, c: number, d: number) => [number, number]; export const mainthreadservercontext_remove_attribute: (a: number, b: number, c: number, d: number) => void; export const mainthreadservercontext_remove_child: (a: number, b: number, c: number) => void; diff --git a/packages/web-platform/web-core/css/index.css b/packages/web-platform/web-core/css/index.css index ff9985279c..d73bf259ef 100644 --- a/packages/web-platform/web-core/css/index.css +++ b/packages/web-platform/web-core/css/index.css @@ -40,6 +40,11 @@ lynx-view::part(page) { --vh-unit: inherit; } +@property --rem-unit { + syntax: ""; + inherits: true; +} + @property --rpx-unit { syntax: ""; inherits: true; diff --git a/packages/web-platform/web-core/src/main_thread/client/element_apis/style_apis.rs b/packages/web-platform/web-core/src/main_thread/client/element_apis/style_apis.rs index d97e846b51..0f4246813d 100644 --- a/packages/web-platform/web-core/src/main_thread/client/element_apis/style_apis.rs +++ b/packages/web-platform/web-core/src/main_thread/client/element_apis/style_apis.rs @@ -110,12 +110,14 @@ pub fn set_inline_styles_in_str( styles: String, transform_vw: bool, transform_vh: bool, + transform_rem: bool, ) -> bool { let transformed_style_str = transform_inline_style_string( &styles, &crate::style_transformer::token_transformer::TransformerConfig { transform_vw, transform_vh, + transform_rem, }, ); // we compare the transformed style string with the original one @@ -133,12 +135,14 @@ pub fn set_inline_styles_in_key_value_vec( k_v_vec: Vec, transform_vw: bool, transform_vh: bool, + transform_rem: bool, ) { let transformed_style_str = transform_inline_style_key_value_vec( k_v_vec, &crate::style_transformer::token_transformer::TransformerConfig { transform_vw, transform_vh, + transform_rem, }, ); let _ = dom.set_attribute("style", &transformed_style_str); diff --git a/packages/web-platform/web-core/src/main_thread/server/main_thread_server_context.rs b/packages/web-platform/web-core/src/main_thread/server/main_thread_server_context.rs index d7bd8734e8..6afb126254 100644 --- a/packages/web-platform/web-core/src/main_thread/server/main_thread_server_context.rs +++ b/packages/web-platform/web-core/src/main_thread/server/main_thread_server_context.rs @@ -34,6 +34,7 @@ pub struct MainThreadServerContext { enable_css_selector: bool, transform_vw: bool, transform_vh: bool, + transform_rem: bool, } #[wasm_bindgen] @@ -44,6 +45,7 @@ impl MainThreadServerContext { enable_css_selector: bool, transform_vw: bool, transform_vh: bool, + transform_rem: bool, ) -> Self { Self { elements: Vec::new(), @@ -52,6 +54,7 @@ impl MainThreadServerContext { enable_css_selector, transform_vw, transform_vh, + transform_rem, } } @@ -272,6 +275,7 @@ impl MainThreadServerContext { &crate::style_transformer::token_transformer::TransformerConfig { transform_vw: self.transform_vw, transform_vh: self.transform_vh, + transform_rem: self.transform_rem, }, ); element.set_attribute(key, transformed); @@ -329,6 +333,7 @@ impl MainThreadServerContext { &crate::style_transformer::token_transformer::TransformerConfig { transform_vw: self.transform_vw, transform_vh: self.transform_vh, + transform_rem: self.transform_rem, }, ); if transformed_style_str == styles { @@ -346,6 +351,7 @@ impl MainThreadServerContext { &crate::style_transformer::token_transformer::TransformerConfig { transform_vw: self.transform_vw, transform_vh: self.transform_vh, + transform_rem: self.transform_rem, }, ); if let Some(Some(element)) = self.elements.get_mut(element_id) { @@ -524,7 +530,7 @@ mod tests { #[test] fn test_html_generation() { - let mut ctx = MainThreadServerContext::new("".to_string(), true, false, false); + let mut ctx = MainThreadServerContext::new("".to_string(), true, false, false, false); // Create
let div_id = ctx.create_element("div".to_string(), None, None, None); @@ -555,7 +561,7 @@ mod tests { #[test] fn test_set_style_empty_value() { - let mut ctx = MainThreadServerContext::new("".to_string(), true, false, false); + let mut ctx = MainThreadServerContext::new("".to_string(), true, false, false, false); let div_id = ctx.create_element("div".to_string(), None, None, None); // This should not panic diff --git a/packages/web-platform/web-core/src/style_transformer/inline_style.rs b/packages/web-platform/web-core/src/style_transformer/inline_style.rs index a598a79fbf..fd98cfdd9b 100644 --- a/packages/web-platform/web-core/src/style_transformer/inline_style.rs +++ b/packages/web-platform/web-core/src/style_transformer/inline_style.rs @@ -30,6 +30,7 @@ pub(crate) fn transform_inline_style_string(source: &str, config: &TransformerCo TransformerConfig { transform_vw: config.transform_vw, transform_vh: config.transform_vh, + transform_rem: config.transform_rem, }, ); transformer.parse(source); @@ -49,6 +50,7 @@ pub(crate) fn transform_inline_style_key_value_vec( TransformerConfig { transform_vw: config.transform_vw, transform_vh: config.transform_vh, + transform_rem: config.transform_rem, }, ); diff --git a/packages/web-platform/web-core/src/style_transformer/token_transformer.rs b/packages/web-platform/web-core/src/style_transformer/token_transformer.rs index b23ab5f1e9..6dc38d8f27 100644 --- a/packages/web-platform/web-core/src/style_transformer/token_transformer.rs +++ b/packages/web-platform/web-core/src/style_transformer/token_transformer.rs @@ -11,6 +11,7 @@ use std::borrow::Cow; pub struct TransformerConfig { pub transform_vw: bool, pub transform_vh: bool, + pub transform_rem: bool, } /** @@ -40,6 +41,12 @@ pub(crate) fn transform_one_token<'a>( token_type, Cow::Owned(format!("calc({value} * var(--ppx-unit))")), ); + } else if config.transform_rem && suffix.eq_ignore_ascii_case("rem") { + let value = &token_value[..len - 3]; + return ( + token_type, + Cow::Owned(format!("calc({value} * var(--rem-unit))")), + ); } } if len > 2 { @@ -142,6 +149,25 @@ mod tests { assert_eq!(tv, "100vh"); } + #[test] + fn test_transform_rem() { + let (_, tv) = transform_one_token( + DIMENSION_TOKEN, + "2rem", + &TransformerConfig { + transform_rem: true, + ..Default::default() + }, + ); + assert_eq!(tv, "calc(2 * var(--rem-unit))"); + } + + #[test] + fn test_transform_rem_disabled() { + let (_, tv) = transform_one_token(DIMENSION_TOKEN, "2rem", &TransformerConfig::default()); + assert_eq!(tv, "2rem"); + } + #[test] fn test_transform_vw_case_insensitive() { let (_, tv) = transform_one_token( diff --git a/packages/web-platform/web-core/src/template/template_sections/style_info/decoded_style_data.rs b/packages/web-platform/web-core/src/template/template_sections/style_info/decoded_style_data.rs index cbaa320d70..d28eef506f 100644 --- a/packages/web-platform/web-core/src/template/template_sections/style_info/decoded_style_data.rs +++ b/packages/web-platform/web-core/src/template/template_sections/style_info/decoded_style_data.rs @@ -48,6 +48,7 @@ pub fn decode_style_info( config_enable_css_selector: bool, transform_vw: bool, transform_vh: bool, + transform_rem: bool, ) -> Result { let buf = buffer.to_vec(); let data = unsafe { rkyv::from_bytes_unchecked::(&buf) } @@ -59,6 +60,7 @@ pub fn decode_style_info( config_enable_css_selector, transform_vw, transform_vh, + transform_rem, )? .into(); @@ -76,6 +78,7 @@ pub fn encode_legacy_json_generated_raw_style_info( entry_name: Option, transform_vw: bool, transform_vh: bool, + transform_rem: bool, ) -> Result { let decode_data: DecodedStyleData = StyleInfoDecoder::new( raw_style_info, @@ -83,6 +86,7 @@ pub fn encode_legacy_json_generated_raw_style_info( config_enable_css_selector, transform_vw, transform_vh, + transform_rem, )? .into(); let serialized = rkyv::to_bytes::<_, 1024>(&decode_data).map_err(|e| { diff --git a/packages/web-platform/web-core/src/template/template_sections/style_info/raw_style_info.rs b/packages/web-platform/web-core/src/template/template_sections/style_info/raw_style_info.rs index 4dc4d92c95..4c766271fd 100644 --- a/packages/web-platform/web-core/src/template/template_sections/style_info/raw_style_info.rs +++ b/packages/web-platform/web-core/src/template/template_sections/style_info/raw_style_info.rs @@ -141,7 +141,7 @@ impl RawStyleInfo { */ #[cfg(feature = "encode")] pub fn encode(&mut self) -> Result { - let decoded_style_info = StyleInfoDecoder::new(self.clone(), None, true, false, false)?; + let decoded_style_info = StyleInfoDecoder::new(self.clone(), None, true, false, false, false)?; self.style_content_str_size_hint = decoded_style_info.style_content.len(); let serialized = rkyv::to_bytes::<_, 1024>(self) .map_err(|e| JsError::new(&format!("Failed to encode RawStyleInfo: {e:?}")))?; diff --git a/packages/web-platform/web-core/src/template/template_sections/style_info/style_info_decoder.rs b/packages/web-platform/web-core/src/template/template_sections/style_info/style_info_decoder.rs index eceeee5e42..418e829364 100644 --- a/packages/web-platform/web-core/src/template/template_sections/style_info/style_info_decoder.rs +++ b/packages/web-platform/web-core/src/template/template_sections/style_info/style_info_decoder.rs @@ -28,6 +28,7 @@ pub struct StyleInfoDecoder { config_enable_css_selector: bool, transform_vw: bool, transform_vh: bool, + transform_rem: bool, entry_name: Option, css_og_current_processing_css_ids: Option>, css_og_current_processing_class_selector_names: Option>, @@ -40,6 +41,7 @@ impl StyleInfoDecoder { config_enable_css_selector: bool, transform_vw: bool, transform_vh: bool, + transform_rem: bool, ) -> Result { let flattened_style_info: FlattenedStyleInfo = raw_style_info.into(); let mut decoded_style_info = StyleInfoDecoder { @@ -55,6 +57,7 @@ impl StyleInfoDecoder { config_enable_css_selector, transform_vw, transform_vh, + transform_rem, is_processing_font_face: false, css_og_current_processing_css_ids: None, css_og_current_processing_class_selector_names: None, @@ -288,6 +291,7 @@ impl StyleInfoDecoder { crate::style_transformer::token_transformer::TransformerConfig { transform_vw: self.transform_vw, transform_vh: self.transform_vh, + transform_rem: self.transform_rem, }, ); @@ -383,6 +387,7 @@ mod test { config_enable_css_selector, false, false, + false, ) .unwrap() } @@ -911,7 +916,7 @@ mod tests_roundtrip { let decoded_raw = unsafe { rkyv::from_bytes_unchecked::(&buf) } .expect("RawStyleInfo decode should succeed"); - let decoder = StyleInfoDecoder::new(decoded_raw, None, true, false, false) + let decoder = StyleInfoDecoder::new(decoded_raw, None, true, false, false, false) .expect("StyleInfoDecoder should succeed"); let decoded_string = decoder.style_content; @@ -962,7 +967,7 @@ mod tests_roundtrip { let decoded_raw = unsafe { rkyv::from_bytes_unchecked::(&buf) } .expect("RawStyleInfo decode should succeed"); - let decoder = StyleInfoDecoder::new(decoded_raw, None, true, false, false) + let decoder = StyleInfoDecoder::new(decoded_raw, None, true, false, false, false) .expect("StyleInfoDecoder should succeed"); let decoded_string = decoder.style_content; diff --git a/packages/web-platform/web-core/tests/__snapshots__/encode.spec.ts.snap b/packages/web-platform/web-core/tests/__snapshots__/encode.spec.ts.snap index 82f9a8cbf4..c55388634d 100644 --- a/packages/web-platform/web-core/tests/__snapshots__/encode.spec.ts.snap +++ b/packages/web-platform/web-core/tests/__snapshots__/encode.spec.ts.snap @@ -21,3 +21,5 @@ exports[`encodeCSS > scoped css 1`] = `".foo:where([l-css-id="1"]):not([l-e-name exports[`encodeCSS > scoped css, 2 css id 1`] = `".foo:where([l-css-id="1"]):not([l-e-name]),.foo:where([l-css-id="2"]):not([l-e-name]){background:red;}"`; exports[`encodeCSS > scoped css, import non existing 1`] = `".foo:where([l-css-id="1"]):not([l-e-name]),.foo:where([l-css-id="20"]):not([l-e-name]),.foo:where([l-css-id="2"]):not([l-e-name]){background:red;}"`; + +exports[`encodeCSS > should encode KeyframesRule with CSS variables 1`] = `"@keyframes my-animation-with-vars{from{--my-var:0;opacity:{{--my-var}};}to{--my-var:1;opacity:{{--my-var}};}}"`; diff --git a/packages/web-platform/web-core/tests/deploy.spec.ts b/packages/web-platform/web-core/tests/deploy.spec.ts new file mode 100644 index 0000000000..8e07ebf844 --- /dev/null +++ b/packages/web-platform/web-core/tests/deploy.spec.ts @@ -0,0 +1,60 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { executeTemplate } from '../ts/server/deploy.js'; +import * as decodeModule from '../ts/server/decode.js'; +import type { DecodedTemplate } from '../ts/server/decode.js'; +import * as createElementAPIModule from '../ts/server/elementAPIs/createElementAPI.js'; +import type { MainThreadServerContext } from '../ts/server/wasm.js'; +import type { ElementPAPIs } from '../ts/types/index.js'; + +vi.mock('../ts/server/decode.js', () => ({ + decodeTemplate: vi.fn(), +})); + +vi.mock('../ts/server/elementAPIs/createElementAPI.js', () => ({ + createElementAPI: vi.fn(), +})); + +vi.mock('vm', () => ({ + createContext: vi.fn(), + runInContext: vi.fn(), +})); + +describe('executeTemplate', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should pass viewAttributes correctly when called with 7 arguments (legacy call)', () => { + const mockDecodeTemplate = vi.mocked(decodeModule.decodeTemplate); + const mockCreateElementAPI = vi.mocked( + createElementAPIModule.createElementAPI, + ); + + mockDecodeTemplate.mockReturnValue({ + config: { enableCSSSelector: 'true' }, + lepusCode: {}, + styleInfo: new Uint8Array(), + } as DecodedTemplate); + + mockCreateElementAPI.mockReturnValue({ + globalThisAPIs: {} as ElementPAPIs, + wasmContext: {} as MainThreadServerContext, + }); + + const dummyBuffer = Buffer.from('test'); + + executeTemplate( + dummyBuffer, + {}, + {}, + () => {}, + true, // transformVW + true, // transformVH + 'my-view-attr="123"', // viewAttributes + ); + + expect(mockCreateElementAPI).toHaveBeenCalled(); + // 3rd arg is viewAttributes + expect(mockCreateElementAPI.mock.calls[0][2]).toBe('my-view-attr="123"'); + }); +}); diff --git a/packages/web-platform/web-core/tests/encode.spec.ts b/packages/web-platform/web-core/tests/encode.spec.ts index 9c6761ba27..6737ec6d61 100644 --- a/packages/web-platform/web-core/tests/encode.spec.ts +++ b/packages/web-platform/web-core/tests/encode.spec.ts @@ -124,6 +124,31 @@ describe('encodeCSS', () => { expect(buffer.length).toBeGreaterThan(0); }); + test('should encode KeyframesRule with CSS variables', () => { + const css = ` + @keyframes my-animation-with-vars { + from { + --my-var: 0; + opacity: var(--my-var); + } + to { + --my-var: 1; + opacity: var(--my-var); + } + } + `; + const cssMap = { + '1': CSS.parse(css).root, + }; + const buffer = encodeCSS(cssMap); + expect(buffer).toBeInstanceOf(Uint8Array); + expect(buffer.length).toBeGreaterThan(0); + const decodedString = get_style_content( + decode_style_info(buffer, undefined, true), + ); + expect(decodedString.trim()).toMatchSnapshot(); + }); + test('should handle complex selectors', () => { const css = ` div > .foo + #bar[attr="val"]::before:hover { diff --git a/packages/web-platform/web-core/tests/template-manager.spec.ts b/packages/web-platform/web-core/tests/template-manager.spec.ts index b8402996ed..a45f35314b 100644 --- a/packages/web-platform/web-core/tests/template-manager.spec.ts +++ b/packages/web-platform/web-core/tests/template-manager.spec.ts @@ -87,6 +87,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, ); // Verify data using getCustomSection @@ -125,6 +126,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, ), ) .rejects.toThrow('Unsupported version: 2'); @@ -169,6 +171,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, ); // Verify data using getCustomSection @@ -225,6 +228,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, ), ).rejects.toThrow('Stream failed'); @@ -258,6 +262,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, overrideConfig as any, ); @@ -333,6 +338,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, ); // Verify config @@ -384,6 +390,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, ); // Verify config has appType = lazy and isLazy = true @@ -432,12 +439,14 @@ describe('Template Manager', () => { Promise.resolve(instance1 as unknown as LynxViewInstance), false, false, + false, ), templateManager.fetchBundle( 'http://example.com/template_concurrent', Promise.resolve(instance2 as unknown as LynxViewInstance), false, false, + false, ), ]); diff --git a/packages/web-platform/web-core/ts/client/decodeWorker/cssLoader.ts b/packages/web-platform/web-core/ts/client/decodeWorker/cssLoader.ts index a88dafe470..9c71809cae 100644 --- a/packages/web-platform/web-core/ts/client/decodeWorker/cssLoader.ts +++ b/packages/web-platform/web-core/ts/client/decodeWorker/cssLoader.ts @@ -20,6 +20,7 @@ export function loadStyleFromJSON( configEnableCSSSelector: boolean, transformVW: boolean, transformVH: boolean, + transformREM: boolean, entryName?: string, ): Uint8Array { const rawStyleInfo = new wasmInstance.RawStyleInfo(); @@ -105,6 +106,7 @@ export function loadStyleFromJSON( entryName, transformVW, transformVH, + transformREM, ); } diff --git a/packages/web-platform/web-core/ts/client/decodeWorker/decode.worker.ts b/packages/web-platform/web-core/ts/client/decodeWorker/decode.worker.ts index 7fc4914cab..600e3fb5c9 100644 --- a/packages/web-platform/web-core/ts/client/decodeWorker/decode.worker.ts +++ b/packages/web-platform/web-core/ts/client/decodeWorker/decode.worker.ts @@ -106,7 +106,14 @@ self.onmessage = async ( wasmInstance.initSync({ module: wasmModule }); wasmModuleLoadedResolve(); } else if (data.type === 'load') { - const { url, fetchUrl, overrideConfig, transformVW, transformVH } = data; + const { + url, + fetchUrl, + overrideConfig, + transformVW, + transformVH, + transformREM, + } = data; try { const response = await fetch(fetchUrl, { headers: { @@ -117,7 +124,14 @@ self.onmessage = async ( throw new Error(`Failed to fetch template: ${response.statusText}`); } const reader = response.body.getReader(); - await handleStream(url, reader, transformVW, transformVH, overrideConfig); + await handleStream( + url, + reader, + transformVW, + transformVH, + transformREM, + overrideConfig, + ); postMessage({ type: 'done', url } as MainMessage); } catch (error) { postMessage( @@ -131,6 +145,7 @@ async function handleStream( reader: ReadableStreamDefaultReader, transformVW: boolean, transformVH: boolean, + transformREM: boolean, overrideConfig?: Partial, ) { const streamReader = new StreamReader(reader); @@ -148,7 +163,14 @@ async function handleStream( const decoder = new TextDecoder(); const jsonStr = decoder.decode(headerBytes) + decoder.decode(rest); const json = JSON.parse(jsonStr); - await handleJSON(json, url, transformVW, transformVH, overrideConfig); + await handleJSON( + json, + url, + transformVW, + transformVH, + transformREM, + overrideConfig, + ); return; } @@ -227,6 +249,7 @@ async function handleStream( config['enableCSSSelector'] === 'true', transformVW, transformVH, + transformREM, ); postMessage( { @@ -313,6 +336,7 @@ async function handleJSON( url: string, transformVW: boolean, transformVH: boolean, + transformREM: boolean, overrideConfig?: Partial, ) { // Configurations @@ -350,6 +374,7 @@ async function handleJSON( config['enableCSSSelector'] === 'true', transformVW, transformVH, + transformREM, config['isLazy'] === 'true' ? url : undefined, ); postMessage( diff --git a/packages/web-platform/web-core/ts/client/decodeWorker/types.ts b/packages/web-platform/web-core/ts/client/decodeWorker/types.ts index a442481d99..3cdef345a6 100644 --- a/packages/web-platform/web-core/ts/client/decodeWorker/types.ts +++ b/packages/web-platform/web-core/ts/client/decodeWorker/types.ts @@ -15,6 +15,7 @@ export interface LoadTemplateMessage extends DecodeWorkerMessage { fetchUrl: string; transformVW: boolean; transformVH: boolean; + transformREM: boolean; overrideConfig?: Record; } diff --git a/packages/web-platform/web-core/ts/client/mainthread/LynxView.ts b/packages/web-platform/web-core/ts/client/mainthread/LynxView.ts index d871057e08..d58c376f21 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/LynxView.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/LynxView.ts @@ -75,6 +75,7 @@ export class LynxViewElement extends HTMLElement { 'browser-config', 'transform-vw', 'transform-vh', + 'transform-rem', ]; /** * @private @@ -163,6 +164,24 @@ export class LynxViewElement extends HTMLElement { } } + #transformREM: boolean = false; + /** + * @public + * @property transformREM + * Enable evaluating rem unit to the current CSS var(--rem-unit) + */ + get transformREM(): boolean { + return this.#transformREM; + } + set transformREM(val: boolean) { + this.#transformREM = val; + if (val) { + this.setAttribute('transform-rem', ''); + } else { + this.removeAttribute('transform-rem'); + } + } + constructor() { super(); if (!this.onNativeModulesCall) { @@ -373,6 +392,9 @@ export class LynxViewElement extends HTMLElement { case 'transform-vh': this.transformVH = newValue !== 'false' && newValue !== null; break; + case 'transform-rem': + this.transformREM = newValue !== 'false' && newValue !== null; + break; } } } @@ -459,6 +481,7 @@ export class LynxViewElement extends HTMLElement { this.#initI18nResources, this.transformVW, this.transformVH, + this.transformREM, this.browserConfig, ); }); @@ -467,6 +490,7 @@ export class LynxViewElement extends HTMLElement { lynxViewInstance, this.transformVW, this.transformVH, + this.transformREM, undefined, // overrideConfig ); @@ -493,6 +517,7 @@ export class LynxViewElement extends HTMLElement { this.#upgradeProperty('browserConfig'); this.#upgradeProperty('transformVW'); this.#upgradeProperty('transformVH'); + this.#upgradeProperty('transformREM'); if (this.url) { this.#url = this.url; } diff --git a/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts b/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts index fe18f9b244..ab3a7eabaa 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts @@ -86,6 +86,7 @@ export class LynxViewInstance implements AsyncDisposable { initI18nResources?: InitI18nResources, private readonly transformVW: boolean = false, private readonly transformVH: boolean = false, + private readonly transformREM: boolean = false, browserConfig?: Record, ) { this.systemInfo = createSystemInfo(browserConfig); @@ -132,6 +133,7 @@ export class LynxViewInstance implements AsyncDisposable { defaultOverflowVisible, this.transformVW, this.transformVH, + this.transformREM, ), createMainThreadGlobalAPIs( this, @@ -233,6 +235,7 @@ export class LynxViewInstance implements AsyncDisposable { Promise.resolve(this), this.transformVW, this.transformVH, + this.transformREM, { enableCSSSelector: this.#pageConfig!['enableCSSSelector'], }, diff --git a/packages/web-platform/web-core/ts/client/mainthread/TemplateManager.ts b/packages/web-platform/web-core/ts/client/mainthread/TemplateManager.ts index 0bb03322d1..2ac62db93b 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/TemplateManager.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/TemplateManager.ts @@ -49,6 +49,7 @@ export class TemplateManager { lynxViewInstancePromise: Promise, transformVW: boolean, transformVH: boolean, + transformREM: boolean, overrideConfig?: Record, ): Promise { if (this.#bundles.has(url) && !overrideConfig) { @@ -80,6 +81,7 @@ export class TemplateManager { lynxViewInstancePromise, transformVW, transformVH, + transformREM, overrideConfig, ); this.#loadingPromises.set(url, promise); @@ -92,6 +94,7 @@ export class TemplateManager { lynxViewInstancePromise: Promise, transformVW: boolean, transformVH: boolean, + transformREM: boolean, overrideConfig?: Partial, ): Promise { const currentTime = performance.now() + performance.timeOrigin; @@ -112,6 +115,7 @@ export class TemplateManager { fetchUrl: (new URL(url, location.href)).toString(), transformVW, transformVH, + transformREM, overrideConfig, }; this.#worker!.postMessage(msg); diff --git a/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/createElementAPI.ts b/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/createElementAPI.ts index a3c7d7ebc8..27ab251815 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/createElementAPI.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/createElementAPI.ts @@ -66,6 +66,7 @@ export function createElementAPI( config_default_overflow_visible: boolean, transform_vw: boolean, transform_vh: boolean, + transform_rem: boolean, ): ElementPAPIs { const wasmContext = new MainThreadWasmContext( rootDom, @@ -294,6 +295,7 @@ export function createElementAPI( value, transform_vw, transform_vh, + transform_rem, ) ) { element.setAttribute('style', value); @@ -312,6 +314,7 @@ export function createElementAPI( vec, transform_vw, transform_vh, + transform_rem, ); } } diff --git a/packages/web-platform/web-core/ts/encode/encodeCSS.ts b/packages/web-platform/web-core/ts/encode/encodeCSS.ts index 78d4b009b1..fb75c70d57 100644 --- a/packages/web-platform/web-core/ts/encode/encodeCSS.ts +++ b/packages/web-platform/web-core/ts/encode/encodeCSS.ts @@ -59,6 +59,12 @@ export function encodeCSS( keyFrameChildrenRule.set_prelude(prelude); + for ( + const [key, value] of Object.entries(keyframesStyle.variables ?? {}) + ) { + keyFrameChildrenRule.push_declaration(key, value); + } + for (const decl of keyframesStyle.style) { keyFrameChildrenRule.push_declaration(decl.name, decl.value); } diff --git a/packages/web-platform/web-core/ts/server/decode.ts b/packages/web-platform/web-core/ts/server/decode.ts index f929b72cc9..df64244064 100644 --- a/packages/web-platform/web-core/ts/server/decode.ts +++ b/packages/web-platform/web-core/ts/server/decode.ts @@ -18,6 +18,7 @@ export function decodeTemplate( buffer: Uint8Array, transformVW: boolean, transformVH: boolean, + transformREM: boolean, ): DecodedTemplate { if (buffer.length < 8) { throw new Error('Buffer too short for Magic Header'); @@ -89,6 +90,7 @@ export function decodeTemplate( config['enableCSSSelector'] === 'true', transformVW, transformVH, + transformREM, ); styleInfo = buffer; break; diff --git a/packages/web-platform/web-core/ts/server/deploy.ts b/packages/web-platform/web-core/ts/server/deploy.ts index 9c5e28bf78..8cf6c27f0e 100644 --- a/packages/web-platform/web-core/ts/server/deploy.ts +++ b/packages/web-platform/web-core/ts/server/deploy.ts @@ -15,8 +15,14 @@ export function executeTemplate( transformVW: boolean, transformVH: boolean, viewAttributes?: string, + transformREM?: boolean, ): string | undefined { - const result = decodeTemplate(templateBuffer, transformVW, transformVH); + const result = decodeTemplate( + templateBuffer, + transformVW, + transformVH, + !!transformREM, + ); const config = result.config; const binding: SSRBinding = { ssrResult: '' }; @@ -30,6 +36,7 @@ export function executeTemplate( defaultDisplayLinear: config['defaultDisplayLinear'] !== 'false', // Default to true if not present or 'true' transformVW: transformVW, transformVH: transformVH, + transformREM: !!transformREM, }, ); diff --git a/packages/web-platform/web-core/ts/server/elementAPIs/createElementAPI.ts b/packages/web-platform/web-core/ts/server/elementAPIs/createElementAPI.ts index a6f120d7df..7caee0ac37 100644 --- a/packages/web-platform/web-core/ts/server/elementAPIs/createElementAPI.ts +++ b/packages/web-platform/web-core/ts/server/elementAPIs/createElementAPI.ts @@ -98,6 +98,7 @@ export function createElementAPI( defaultDisplayLinear: boolean; transformVW: boolean; transformVH: boolean; + transformREM: boolean; }, ): { globalThisAPIs: ElementPAPIs; wasmContext: MainThreadServerContext } { const wasmContext = new MainThreadServerContext( @@ -105,6 +106,7 @@ export function createElementAPI( config.enableCSSSelector, config.transformVW, config.transformVH, + config.transformREM, ); if (styleInfo) { const resource = new StyleSheetResource(styleInfo, undefined);