@rohal12/spindle 0.51.1 → 0.51.2

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.
@@ -4825,16 +4825,17 @@ function defineMacro(config, source = "builtin") {
4825
4825
  //#endregion
4826
4826
  //#region src/passage-render-state.ts
4827
4827
  /**
4828
- * Name of the passage most recently mounted by <Passage> (null until the
4829
- * first render, e.g. when the story interface has no {passage}).
4830
- * Lets Story.waitForActions() tell whether the current passage is on screen.
4828
+ * Navigation id (the store's `navigationId`) of the passage most recently
4829
+ * mounted by <Passage> (null until the first render, e.g. when the story
4830
+ * interface has no {passage}). Lets Story.waitForActions() tell whether the
4831
+ * current navigation is on screen, also when it revisits the passage shown.
4831
4832
  */
4832
- var renderedPassage = null;
4833
- function markPassageRendered(name) {
4834
- renderedPassage = name;
4833
+ var renderedNavigationId = null;
4834
+ function markPassageRendered(navigationId) {
4835
+ renderedNavigationId = navigationId;
4835
4836
  }
4836
- function getRenderedPassage() {
4837
- return renderedPassage;
4837
+ function getRenderedNavigationId() {
4838
+ return renderedNavigationId;
4838
4839
  }
4839
4840
  //#endregion
4840
4841
  //#region src/story-api.ts
@@ -5128,11 +5129,10 @@ function createStoryAPI() {
5128
5129
  await new Promise((resolve) => {
5129
5130
  requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
5130
5131
  });
5131
- const { currentPassage, renderDeferred } = useStoryStore.getState();
5132
- const rendered = getRenderedPassage();
5133
- if (rendered !== null && rendered !== currentPassage && !renderDeferred) await new Promise((resolve) => {
5134
- const off = on("passagerender", (name) => {
5135
- if (name === useStoryStore.getState().currentPassage) {
5132
+ const isRendered = () => getRenderedNavigationId() === useStoryStore.getState().navigationId;
5133
+ if (getRenderedNavigationId() !== null && !isRendered() && !useStoryStore.getState().renderDeferred) await new Promise((resolve) => {
5134
+ const off = on("passagerender", () => {
5135
+ if (isRendered()) {
5136
5136
  off();
5137
5137
  resolve();
5138
5138
  }
@@ -7152,7 +7152,7 @@ var CODE_PASSAGES = new Set([
7152
7152
  "PassageFooter",
7153
7153
  "PassageDone"
7154
7154
  ]);
7155
- function Passage({ passage, dataTransition }) {
7155
+ function Passage({ passage, dataTransition, navigationId }) {
7156
7156
  const storyData = useStoryStore((s) => s.storyData);
7157
7157
  const isCodePassage = CODE_PASSAGES.has(passage.name);
7158
7158
  const [doneReady, setDoneReady] = useState(false);
@@ -7210,9 +7210,9 @@ function Passage({ passage, dataTransition }) {
7210
7210
  }, [doneReady, donePassage?.content]);
7211
7211
  const elRef = useRef(null);
7212
7212
  useLayoutEffect(() => {
7213
- markPassageRendered(passage.name);
7213
+ if (navigationId !== void 0) markPassageRendered(navigationId);
7214
7214
  if (elRef.current) emitFromRender("passagerender", passage.name, elRef.current);
7215
- }, [passage.name]);
7215
+ }, [passage.name, navigationId]);
7216
7216
  const nobr = passage.tags.includes("nobr");
7217
7217
  const inner = /* @__PURE__ */ jsxs("div", {
7218
7218
  ref: elRef,
@@ -7458,7 +7458,8 @@ defineMacro({
7458
7458
  ref: containerRef,
7459
7459
  children: effectivePassage && /* @__PURE__ */ jsx(Passage, {
7460
7460
  passage: effectivePassage,
7461
- dataTransition: renderDeferred ? "none" : resolvedTypeRef.current
7461
+ dataTransition: renderDeferred ? "none" : resolvedTypeRef.current,
7462
+ navigationId: renderDeferred ? void 0 : displayed.id
7462
7463
  }, renderDeferred ? "loading" : `nav-${displayed.id}`)
7463
7464
  })]
7464
7465
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rohal12/spindle",
3
- "version": "0.51.1",
3
+ "version": "0.51.2",
4
4
  "type": "module",
5
5
  "description": "A Preact-based story format for Twine 2.",
6
6
  "license": "Unlicense",
@@ -42,6 +42,8 @@ export function renderPassageContent(passage: PassageData) {
42
42
  interface PassageProps {
43
43
  passage: PassageData;
44
44
  dataTransition?: string;
45
+ /** The navigation this passage displays; omitted for placeholders. */
46
+ navigationId?: number;
45
47
  }
46
48
 
47
49
  const CODE_PASSAGES = new Set([
@@ -51,7 +53,11 @@ const CODE_PASSAGES = new Set([
51
53
  'PassageDone',
52
54
  ]);
53
55
 
54
- export function Passage({ passage, dataTransition }: PassageProps) {
56
+ export function Passage({
57
+ passage,
58
+ dataTransition,
59
+ navigationId,
60
+ }: PassageProps) {
55
61
  const storyData = useStoryStore((s) => s.storyData);
56
62
  const isCodePassage = CODE_PASSAGES.has(passage.name);
57
63
  const [doneReady, setDoneReady] = useState(false);
@@ -121,11 +127,11 @@ export function Passage({ passage, dataTransition }: PassageProps) {
121
127
  // Passage so every navigation that shows a passage mounts a new one.
122
128
  const elRef = useRef<HTMLDivElement>(null);
123
129
  useLayoutEffect(() => {
124
- markPassageRendered(passage.name);
130
+ if (navigationId !== undefined) markPassageRendered(navigationId);
125
131
  if (elRef.current) {
126
132
  emitFromRender('passagerender', passage.name, elRef.current);
127
133
  }
128
- }, [passage.name]);
134
+ }, [passage.name, navigationId]);
129
135
 
130
136
  const nobr = passage.tags.includes('nobr');
131
137
 
@@ -292,6 +292,7 @@ defineMacro({
292
292
  passage={effectivePassage}
293
293
  key={renderDeferred ? 'loading' : `nav-${displayed.id}`}
294
294
  dataTransition={renderDeferred ? 'none' : resolvedTypeRef.current}
295
+ navigationId={renderDeferred ? undefined : displayed.id}
295
296
  />
296
297
  )}
297
298
  </div>
@@ -1,14 +1,15 @@
1
1
  /**
2
- * Name of the passage most recently mounted by <Passage> (null until the
3
- * first render, e.g. when the story interface has no {passage}).
4
- * Lets Story.waitForActions() tell whether the current passage is on screen.
2
+ * Navigation id (the store's `navigationId`) of the passage most recently
3
+ * mounted by <Passage> (null until the first render, e.g. when the story
4
+ * interface has no {passage}). Lets Story.waitForActions() tell whether the
5
+ * current navigation is on screen, also when it revisits the passage shown.
5
6
  */
6
- let renderedPassage: string | null = null;
7
+ let renderedNavigationId: number | null = null;
7
8
 
8
- export function markPassageRendered(name: string): void {
9
- renderedPassage = name;
9
+ export function markPassageRendered(navigationId: number): void {
10
+ renderedNavigationId = navigationId;
10
11
  }
11
12
 
12
- export function getRenderedPassage(): string | null {
13
- return renderedPassage;
13
+ export function getRenderedNavigationId(): number | null {
14
+ return renderedNavigationId;
14
15
  }
package/src/story-api.ts CHANGED
@@ -30,7 +30,7 @@ import type { MacroDefinition } from './define-macro';
30
30
  import { getMacroRegistry as _getMacroRegistry } from './registry';
31
31
  import type { MacroMetadata } from './registry';
32
32
  import { getActions, getAction, type StoryAction } from './action-registry';
33
- import { getRenderedPassage } from './passage-render-state';
33
+ import { getRenderedNavigationId } from './passage-render-state';
34
34
  import {
35
35
  initPRNG,
36
36
  isPRNGEnabled,
@@ -548,13 +548,19 @@ function createStoryAPI(): StoryAPI {
548
548
  });
549
549
  // A navigation can still be rendering: the passage display mounts the
550
550
  // new passage from an effect, after a fade-through's outgoing phase.
551
- // Its actions exist only once it is mounted, so wait for that.
552
- const { currentPassage, renderDeferred } = useStoryStore.getState();
553
- const rendered = getRenderedPassage();
554
- if (rendered !== null && rendered !== currentPassage && !renderDeferred) {
551
+ // Its actions exist only once it is mounted, so wait for that. Compare
552
+ // navigations, not passage names: revisiting the passage shown mounts
553
+ // it anew (#228).
554
+ const isRendered = () =>
555
+ getRenderedNavigationId() === useStoryStore.getState().navigationId;
556
+ if (
557
+ getRenderedNavigationId() !== null &&
558
+ !isRendered() &&
559
+ !useStoryStore.getState().renderDeferred
560
+ ) {
555
561
  await new Promise<void>((resolve) => {
556
- const off = emitterOn('passagerender', (name) => {
557
- if (name === useStoryStore.getState().currentPassage) {
562
+ const off = emitterOn('passagerender', () => {
563
+ if (isRendered()) {
558
564
  off();
559
565
  resolve();
560
566
  }