foldkit 0.82.5 → 0.82.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Effect, SubscriptionRef } from 'effect';
|
|
2
|
+
export type RenderLoopConfig = Readonly<{
|
|
3
|
+
/** SubscriptionRef holding the dirty bit. The loop subscribes to its changes
|
|
4
|
+
* Stream so it can suspend entirely when the bit stays false. */
|
|
5
|
+
pendingRef: SubscriptionRef.SubscriptionRef<boolean>;
|
|
6
|
+
/** Effect that yields once the next animation frame fires. */
|
|
7
|
+
awaitNextFrame: Effect.Effect<void>;
|
|
8
|
+
/** Effect that returns whether the runtime is paused (e.g. via DevTools
|
|
9
|
+
* time-travel). When true, the body skips render but still clears the bit. */
|
|
10
|
+
isPaused: Effect.Effect<boolean>;
|
|
11
|
+
/** Effect that performs one render with the latest model. */
|
|
12
|
+
render: Effect.Effect<void>;
|
|
13
|
+
}>;
|
|
14
|
+
/** Builds an idle-suspending render loop driven by a SubscriptionRef of
|
|
15
|
+
* "render is pending."
|
|
16
|
+
*
|
|
17
|
+
* The loop subscribes to the ref's changes Stream and runs the body once per
|
|
18
|
+
* false-to-true transition. Stream.changes filters consecutive equals so a
|
|
19
|
+
* burst of `set(true)` calls inside one frame produces a single body run.
|
|
20
|
+
*
|
|
21
|
+
* The body waits for the next animation frame, clears the dirty bit, then
|
|
22
|
+
* renders if not paused. The bit is cleared on every body run (including
|
|
23
|
+
* paused early-returns) so the next dispatch is always a real false-to-true
|
|
24
|
+
* transition. This makes the loop self-recovering even when paused state
|
|
25
|
+
* flips externally without a synchronous render. */
|
|
26
|
+
export declare const makeRenderLoop: ({ pendingRef, awaitNextFrame, isPaused, render, }: RenderLoopConfig) => Effect.Effect<void>;
|
|
27
|
+
//# sourceMappingURL=renderLoop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderLoop.d.ts","sourceRoot":"","sources":["../../src/runtime/renderLoop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,eAAe,EAAE,MAAM,QAAQ,CAAA;AAExD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC;sEACkE;IAClE,UAAU,EAAE,eAAe,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;IACpD,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACnC;mFAC+E;IAC/E,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAChC,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;CAC5B,CAAC,CAAA;AAEF;;;;;;;;;;;qDAWqD;AACrD,eAAO,MAAM,cAAc,GAAI,mDAK5B,gBAAgB,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAgBvC,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Effect, Stream, SubscriptionRef } from 'effect';
|
|
2
|
+
/** Builds an idle-suspending render loop driven by a SubscriptionRef of
|
|
3
|
+
* "render is pending."
|
|
4
|
+
*
|
|
5
|
+
* The loop subscribes to the ref's changes Stream and runs the body once per
|
|
6
|
+
* false-to-true transition. Stream.changes filters consecutive equals so a
|
|
7
|
+
* burst of `set(true)` calls inside one frame produces a single body run.
|
|
8
|
+
*
|
|
9
|
+
* The body waits for the next animation frame, clears the dirty bit, then
|
|
10
|
+
* renders if not paused. The bit is cleared on every body run (including
|
|
11
|
+
* paused early-returns) so the next dispatch is always a real false-to-true
|
|
12
|
+
* transition. This makes the loop self-recovering even when paused state
|
|
13
|
+
* flips externally without a synchronous render. */
|
|
14
|
+
export const makeRenderLoop = ({ pendingRef, awaitNextFrame, isPaused, render, }) => {
|
|
15
|
+
const tick = Effect.gen(function* () {
|
|
16
|
+
yield* awaitNextFrame;
|
|
17
|
+
yield* SubscriptionRef.set(pendingRef, false);
|
|
18
|
+
const paused = yield* isPaused;
|
|
19
|
+
if (paused) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
yield* render;
|
|
23
|
+
});
|
|
24
|
+
return SubscriptionRef.changes(pendingRef).pipe(Stream.changes, Stream.filter(isPending => isPending), Stream.runForEach(() => tick));
|
|
25
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,OAAO,EACP,MAAM,EAGN,KAAK,EAEL,MAAM,EAON,MAAM,EAIP,MAAM,QAAQ,CAAA;AAGf,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAIlD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,GAAG,EAA+B,MAAM,iBAAiB,CAAA;AAalE,OAAO,KAAK,EAEV,gBAAgB,EACjB,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/runtime.js
CHANGED
|
@@ -11,6 +11,7 @@ import { defaultCrashView, noOpDispatch } from './crashUI.js';
|
|
|
11
11
|
import { deepFreeze } from './deepFreeze.js';
|
|
12
12
|
import { PreserveModelMessage, RequestModelMessage, RestoreModelMessage, } from './hmrProtocol.js';
|
|
13
13
|
import { orderByPriority } from './messagePriority.js';
|
|
14
|
+
import { makeRenderLoop } from './renderLoop.js';
|
|
14
15
|
const DEFAULT_DEV_TOOLS_SHOW = 'Development';
|
|
15
16
|
const DEFAULT_DEV_TOOLS_POSITION = 'BottomRight';
|
|
16
17
|
const DEFAULT_DEV_TOOLS_MODE = 'TimeTravel';
|
|
@@ -142,7 +143,7 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
142
143
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
143
144
|
enqueueHigh(message);
|
|
144
145
|
const dispatch = { dispatchAsync, dispatchSync };
|
|
145
|
-
const isRenderPendingRef = yield*
|
|
146
|
+
const isRenderPendingRef = yield* SubscriptionRef.make(false);
|
|
146
147
|
const lastDirtyMessageRef = yield* Ref.make(Option.none());
|
|
147
148
|
const isPausedEffect = Effect.gen(function* () {
|
|
148
149
|
const maybeStore = yield* Ref.get(maybeDevToolsStoreRef);
|
|
@@ -157,7 +158,7 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
157
158
|
const nextModel = maybeFreezeModel(nextModelRaw);
|
|
158
159
|
if (currentModel !== nextModel) {
|
|
159
160
|
yield* Ref.set(modelRef, nextModel);
|
|
160
|
-
yield*
|
|
161
|
+
yield* SubscriptionRef.set(isRenderPendingRef, true);
|
|
161
162
|
yield* Ref.set(lastDirtyMessageRef, Option.some(message));
|
|
162
163
|
if (!modelEquivalence(currentModel, nextModel)) {
|
|
163
164
|
PubSub.publishUnsafe(modelPubSub, nextModel);
|
|
@@ -214,9 +215,15 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
214
215
|
const [updatedModel] = update(model, message);
|
|
215
216
|
return maybeFreezeModel(updatedModel);
|
|
216
217
|
},
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
218
|
+
// NOTE: clears the dirty bit on direct DevTools renders (jumpTo,
|
|
219
|
+
// resume) so the renderLoop's Stream.changes sees the next
|
|
220
|
+
// dispatch as a real false-to-true transition rather than a
|
|
221
|
+
// deduped no-op.
|
|
222
|
+
render: model => Effect.gen(function* () {
|
|
223
|
+
yield* SubscriptionRef.set(isRenderPendingRef, false);
|
|
224
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
|
|
225
|
+
yield* render(model, Option.none());
|
|
226
|
+
}),
|
|
220
227
|
getCurrentModel: Ref.get(modelRef),
|
|
221
228
|
});
|
|
222
229
|
yield* Ref.set(maybeDevToolsStoreRef, Option.some(devToolsStore));
|
|
@@ -240,21 +247,16 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
240
247
|
// view past threshold. Acceptable for a debug callback; full
|
|
241
248
|
// attribution would require correlating each message with its render
|
|
242
249
|
// contribution, which isn't worth the complexity.
|
|
243
|
-
const renderLoop =
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
yield* Ref.set(isRenderPendingRef, false);
|
|
254
|
-
const model = yield* Ref.get(modelRef);
|
|
255
|
-
const maybeMessage = yield* Ref.get(lastDirtyMessageRef);
|
|
256
|
-
yield* render(model, maybeMessage);
|
|
257
|
-
}));
|
|
250
|
+
const renderLoop = makeRenderLoop({
|
|
251
|
+
pendingRef: isRenderPendingRef,
|
|
252
|
+
awaitNextFrame,
|
|
253
|
+
isPaused: isPausedEffect,
|
|
254
|
+
render: Effect.gen(function* () {
|
|
255
|
+
const model = yield* Ref.get(modelRef);
|
|
256
|
+
const maybeMessage = yield* Ref.get(lastDirtyMessageRef);
|
|
257
|
+
yield* render(model, maybeMessage);
|
|
258
|
+
}),
|
|
259
|
+
});
|
|
258
260
|
yield* Effect.forkDetach(renderLoop);
|
|
259
261
|
addBfcacheRestoreListener();
|
|
260
262
|
if (subscriptions) {
|
|
@@ -321,8 +323,23 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
321
323
|
const processBatch = (batch) => Effect.forEach(orderByPriority(batch), processWithBudget, {
|
|
322
324
|
discard: true,
|
|
323
325
|
});
|
|
326
|
+
// NOTE: Effect 4's `Queue.takeAll` blocks until at least one message
|
|
327
|
+
// arrives (it's `takeBetween(self, 1, ∞)`, not a non-blocking
|
|
328
|
+
// snapshot). For batching we want "give me whatever is currently in
|
|
329
|
+
// the queue, possibly nothing" so we drain via repeated `Queue.poll`
|
|
330
|
+
// until it returns `None`.
|
|
331
|
+
const pollAvailable = Effect.gen(function* () {
|
|
332
|
+
const accumulated = [];
|
|
333
|
+
while (true) {
|
|
334
|
+
const next = yield* Queue.poll(messageQueue);
|
|
335
|
+
if (Option.isNone(next)) {
|
|
336
|
+
return accumulated;
|
|
337
|
+
}
|
|
338
|
+
accumulated.push(next.value);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
324
341
|
const drainQueue = Effect.gen(function* () {
|
|
325
|
-
const batch = yield*
|
|
342
|
+
const batch = yield* pollAvailable;
|
|
326
343
|
if (Array.isReadonlyArrayEmpty(batch)) {
|
|
327
344
|
return;
|
|
328
345
|
}
|
|
@@ -331,7 +348,7 @@ const makeRuntime = ({ Model, flags: resolveFlags, init, update, view, subscript
|
|
|
331
348
|
});
|
|
332
349
|
yield* pipe(Effect.forever(Effect.gen(function* () {
|
|
333
350
|
const first = yield* Queue.take(messageQueue);
|
|
334
|
-
const rest = yield*
|
|
351
|
+
const rest = yield* pollAvailable;
|
|
335
352
|
yield* Ref.set(burstStartedAtRef, performance.now());
|
|
336
353
|
yield* processBatch(Array.prepend(rest, first));
|
|
337
354
|
yield* drainQueue;
|