-
Notifications
You must be signed in to change notification settings - Fork 122
feat: add MainThreadValue #2144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| --- | ||
| '@lynx-js/react': patch | ||
| --- | ||
|
|
||
| Add support for `MainThreadValue` to enable Main Thread Persistent Data other than `MainThreadRef`, to make library developers able to create their own main thread values. | ||
|
|
||
| ``` | ||
| // Library code | ||
| import { MainThreadValue } from '@lynx-js/react' | ||
|
|
||
| class MotionValue extends MainThreadValue { | ||
| static type = '@example/motion-value'; | ||
|
|
||
| constructor(initValue: T) { | ||
| super(initValue, MotionValue.type); | ||
| } | ||
|
|
||
| get value() { | ||
| return this.getValueOnMainThread(); | ||
| } | ||
|
|
||
| set value(v) { | ||
| return this.setValueOnMainThread(v); | ||
| } | ||
| } | ||
|
|
||
| MainThreadValue.register(MotionValue, MotionValue.type); | ||
|
|
||
| export function useMotionValue<T>(initValue: T): MotionValue<T> { | ||
| return useMemo(() => new MotionValue(initValue), []); | ||
| } | ||
|
|
||
| // User code | ||
| function App() { | ||
| const opacity = useMotionValue(1); | ||
|
|
||
| function handleTap() { | ||
| 'main thread' | ||
| opacity.value = 123 | ||
| } | ||
| } | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,28 +1,80 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useCallback, useEffect, useState } from '@lynx-js/react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runOnMainThread, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useCallback, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useState, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from '@lynx-js/react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import './App.css'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import arrow from './assets/arrow.png'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lynxLogo from './assets/lynx-logo.png'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import reactLynxLogo from './assets/react-logo.png'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useMotionValue } from './MotionValue.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import './App.css'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function App() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [alterLogo, setAlterLogo] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // NEW: MotionValue POC (uses .value) - demonstrates extensible main thread values! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const opacity = useMotionValue(1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const tapCount = useMotionValue(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.info('Hello, ReactLynx'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let unsubscribeHandle: (() => void) | undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runOnMainThread(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'main thread'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Demo: Subscribe to value changes (main thread only) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const unsubscribe = opacity.subscribe((newValue) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.info('Opacity changed to:', newValue); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Demo: Subscribe to value changes (main thread only) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const unsubscribeTapCount = tapCount.subscribe((newValue) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.info('TapCount changed to:', newValue); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| unsubscribe(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| unsubscribeTapCount(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })().then((res) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| unsubscribeHandle = res as () => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }).catch(_err => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // omitted | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| void runOnMainThread(unsubscribeHandle!)(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
22
to
+52
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's check the actual code in App.tsx at the specified lines
cat -n examples/react/src/App.tsx | sed -n '1,70p'Repository: lynx-family/lynx-stack Length of output: 2332 🏁 Script executed: # Find where runOnMainThread is defined
fd -t f '*.ts' -o '*.tsx' -o '*.js' | xargs rg -l 'runOnMainThread' | head -20Repository: lynx-family/lynx-stack Length of output: 1352 🏁 Script executed: # Get the definition of runOnMainThread
rg -A 10 'export.*runOnMainThread|function runOnMainThread' --type ts --type tsxRepository: lynx-family/lynx-stack Length of output: 93 🏁 Script executed: # Check the type definitions or interface for runOnMainThread
rg -B 2 -A 5 'runOnMainThread' examples/react/src/App.tsxRepository: lynx-family/lynx-stack Length of output: 592 🏁 Script executed: # Find the runOnMainThread implementation
cat packages/react/runtime/src/worklet/call/runOnMainThread.tsRepository: lynx-family/lynx-stack Length of output: 2409 🏁 Script executed: # Check the test files for runOnMainThread to understand expected behavior
cat packages/react/runtime/__test__/worklet/runOnMainThread.test.jsxRepository: lynx-family/lynx-stack Length of output: 8942 🏁 Script executed: # Also check the basic test
cat packages/react/worklet-runtime/__test__/runOnMainThread.test.jsRepository: lynx-family/lynx-stack Length of output: 1316 🏁 Script executed: # Check the web platform test to understand what runOnMainThread does
cat packages/web-platform/web-core-wasm-e2e/tests/reactlynx/basic-mts-run-on-main-thread/index.jsx | head -80Repository: lynx-family/lynx-stack Length of output: 915 🏁 Script executed: # Look for similar subscription patterns with cleanup
rg -B 3 -A 5 '\.subscribe\(' packages/motion/src/ --type ts --type tsxRepository: lynx-family/lynx-stack Length of output: 93 🏁 Script executed: # Search for other useEffect + runOnMainThread patterns
rg -B 2 -A 8 'useEffect.*runOnMainThread|runOnMainThread.*\.then' --type ts --type tsxRepository: lynx-family/lynx-stack Length of output: 93 🏁 Script executed: # Check if there are other examples in examples directory
find examples -name "*.tsx" -o -name "*.ts" | xargs rg -l "useEffect" | head -5Repository: lynx-family/lynx-stack Length of output: 237 🏁 Script executed: # Search for subscription patterns without type specification
rg '\.subscribe\(' packages/motion/src/ -A 5Repository: lynx-family/lynx-stack Length of output: 126 🏁 Script executed: # Check other example apps for comparison
for file in examples/tailwindcss/src/App.tsx examples/react-lazy-bundle/src/App.tsx examples/react-externals/src/App.tsx; do
echo "=== $file ===";
cat -n "$file" | head -60;
doneRepository: lynx-family/lynx-stack Length of output: 4904 🏁 Script executed: # Look for async patterns with runOnMainThread
rg 'async.*runOnMainThread|runOnMainThread.*async' -B 2 -A 5Repository: lynx-family/lynx-stack Length of output: 3317 Guard main-thread subscription cleanup against race conditions. 🔧 Proposed fix useEffect(() => {
console.info('Hello, ReactLynx');
- let unsubscribeHandle: (() => void) | undefined;
-
- runOnMainThread(() => {
- 'main thread';
-
- // Demo: Subscribe to value changes (main thread only)
- const unsubscribe = opacity.subscribe((newValue) => {
- console.info('Opacity changed to:', newValue);
- });
-
- // Demo: Subscribe to value changes (main thread only)
- const unsubscribeTapCount = tapCount.subscribe((newValue) => {
- console.info('TapCount changed to:', newValue);
- });
-
- return () => {
- unsubscribe();
- unsubscribeTapCount();
- };
- })().then((res) => {
- unsubscribeHandle = res as () => void;
- }).catch(_err => {
- // omitted
- });
-
- return () => {
- void runOnMainThread(unsubscribeHandle!)();
- };
+ let unsubscribeHandle: (() => void) | undefined;
+ let cancelled = false;
+
+ const init = async () => {
+ try {
+ const res = await runOnMainThread(() => {
+ 'main thread';
+
+ // Demo: Subscribe to value changes (main thread only)
+ const unsubscribe = opacity.subscribe((newValue) => {
+ console.info('Opacity changed to:', newValue);
+ });
+
+ // Demo: Subscribe to value changes (main thread only)
+ const unsubscribeTapCount = tapCount.subscribe((newValue) => {
+ console.info('TapCount changed to:', newValue);
+ });
+
+ return () => {
+ unsubscribe();
+ unsubscribeTapCount();
+ };
+ })();
+
+ if (typeof res !== 'function') {
+ return;
+ }
+
+ if (cancelled) {
+ await runOnMainThread(res)();
+ return;
+ }
+
+ unsubscribeHandle = res;
+ } catch (err) {
+ console.error('Main thread subscription failed', err);
+ }
+ };
+
+ void init();
+
+ return () => {
+ cancelled = true;
+ if (unsubscribeHandle) {
+ void runOnMainThread(unsubscribeHandle)().catch((err) => {
+ console.error('Main thread cleanup failed', err);
+ });
+ }
+ };
}, []);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, []); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onTap = useCallback(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'background-only'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setAlterLogo(prevAlterLogo => !prevAlterLogo); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, []); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Demo: MotionValue in tap handler | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onLogoTap = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'main thread'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tapCount.value += 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Animate opacity based on tap count | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| opacity.value = 0.5 + (tapCount.value % 2) * 0.5; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <view> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <view className='Background' /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <view className='App'> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <view className='Banner'> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <view className='Logo' bindtap={onTap}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <view | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className='Logo' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bindtap={onTap} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| main-thread:bindtap={onLogoTap} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {alterLogo | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? <image src={reactLynxLogo} className='Logo--react' /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : <image src={lynxLogo} className='Logo--lynx' />} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| /** | ||
| * MotionValue POC using the MainThreadValue base class. | ||
| * | ||
| * This demonstrates how third-party packages can create their own | ||
| * main thread value types by extending MainThreadValue. | ||
| */ | ||
| import { MainThreadValue, useMemo } from '@lynx-js/react'; | ||
|
|
||
| /** | ||
| * A simple motion value that can be accessed on the main thread. | ||
| * Uses `.value` instead of `.current` to demonstrate the new extensibility. | ||
| */ | ||
| export class MotionValue<T> extends MainThreadValue<T> { | ||
| private _subscribers: Set<(value: T) => void> = new Set(); | ||
|
|
||
| constructor(initValue: T, type = '@example/motion-value') { | ||
| super(initValue, type); | ||
| } | ||
|
|
||
| /** | ||
| * Get the current value. Only works on main thread. | ||
| */ | ||
| get value(): T { | ||
| try { | ||
| return this.getValueOnMainThread(); | ||
| } catch (_e) { | ||
| // Allow access on background thread for capture (returns undefined/garbage, which is ignored) | ||
| return this._initValue; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Set the current value. Only works on main thread. | ||
| * Notifies all subscribers when value changes. | ||
| */ | ||
| set value(v: T) { | ||
| this.setValueOnMainThread(v); | ||
| // Notify subscribers | ||
| this._subscribers.forEach(cb => cb(v)); | ||
| } | ||
|
|
||
| /** | ||
| * Subscribe to value changes. | ||
| * @param callback - Called with the new value when it changes | ||
| * @returns Unsubscribe function | ||
| */ | ||
| subscribe(callback: (value: T) => void): () => void { | ||
| this._subscribers.add(callback); | ||
| return () => { | ||
| this._subscribers.delete(callback); | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| // Register for runtime hydration | ||
| MainThreadValue.register(MotionValue, '@example/motion-value'); | ||
|
|
||
| /** | ||
| * Hook to create a MotionValue that persists across renders. | ||
| */ | ||
| export function useMotionValue<T>(initValue: T): MotionValue<T> { | ||
| return useMemo(() => new MotionValue(initValue), []); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -84,3 +84,4 @@ export { | |
| }; | ||
|
|
||
| export * from './lynx-api.js'; | ||
| export { workletCapture } from './worklet/capture.js'; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| // 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. | ||
|
|
||
| /** | ||
| * Worklet capture helper function. | ||
| * Called by SWC plugin to preserve MainThreadValue objects during closure extraction. | ||
| * @internal | ||
| */ | ||
| // biome-ignore lint/suspicious/noExplicitAny: Called by generated code with dynamic args | ||
| export function workletCapture( | ||
| obj: unknown, | ||
| ...args: unknown[] | ||
| ): unknown { | ||
| // If it's a Main Thread Value (e.g. MotionValue), return it (ID will be serialized) | ||
| if (obj && typeof obj === 'object' && (obj as { __MT_PERSIST__?: boolean }).__MT_PERSIST__) { | ||
| return obj; | ||
| } | ||
| // Otherwise, reconstruct a lightweight object with only the captured properties | ||
| const result: Record<string, unknown> = {}; | ||
| for (let i = 0; i < args.length; i += 2) { | ||
| result[args[i] as string] = args[i + 1]; | ||
| } | ||
| return result; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a language hint to the fenced code block.
This satisfies MD040 and keeps markdown linting clean.
🛠️ Minimal fix
As per coding guidelines, ...
📝 Committable suggestion
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
7-7: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents