pdfjs-reader-core 0.4.0 → 0.4.1

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.
package/README.md CHANGED
@@ -1271,6 +1271,8 @@ default. Clicking it clears every overlay and returns the camera to fit-page.
1271
1271
  | `showSubtitles` | `boolean` | `false` | Render a subtitle bar with the current chunk text |
1272
1272
  | `showExitButton` | `boolean` | `true` | Render the top-right "Reset view" button |
1273
1273
  | `onExitTutorMode` | `() => void` | — | Optional callback fired AFTER the reset — use it to also leave tutor mode |
1274
+ | `backgroundColor` | `string` | `'#ffffff'` | Surround colour visible around the PDF when the viewport is larger than the page fit. v0.4.1+ |
1275
+ | `loadingComponent` | `ReactNode` | default spinner | Custom loading state shown while the PDF document/page is still fetching. v0.4.1+ |
1274
1276
  | `className` | `string` | — | Passes through to the root container for custom theming |
1275
1277
 
1276
1278
  ### LlmConfig
package/dist/index.cjs CHANGED
@@ -13806,7 +13806,20 @@ init_camera_math();
13806
13806
  var DEFAULT_MIN_OVERLAY_MS = 3500;
13807
13807
  var StoryboardEngine = class {
13808
13808
  constructor(deps) {
13809
- this.pendingTimers = /* @__PURE__ */ new Set();
13809
+ /**
13810
+ * Timers that schedule the START of a step (via `setTimeout(runStep, at_ms)`).
13811
+ * These are storyboard-scoped: when a new storyboard arrives, anything still
13812
+ * pending should be abandoned.
13813
+ */
13814
+ this.pendingStepTimers = /* @__PURE__ */ new Set();
13815
+ /**
13816
+ * Timers that auto-REMOVE an already-placed overlay after its visible
13817
+ * duration. Keyed by overlay id so we can cancel one specifically. These are
13818
+ * NOT cancelled when a new storyboard starts — otherwise the still-visible
13819
+ * overlay from the previous beat would get stranded in the store forever
13820
+ * (the "stuck spotlight" bug).
13821
+ */
13822
+ this.overlayRemovalTimers = /* @__PURE__ */ new Map();
13810
13823
  this.currentStoryboardId = 0;
13811
13824
  this.deps = deps;
13812
13825
  }
@@ -13849,13 +13862,13 @@ var StoryboardEngine = class {
13849
13862
  if (storyboardId !== this.currentStoryboardId) return;
13850
13863
  this.runStep(step);
13851
13864
  }, step.at_ms);
13852
- this.pendingTimers.add(timer);
13865
+ this.pendingStepTimers.add(timer);
13853
13866
  }
13854
13867
  const markExecuting = setTimeout(() => {
13855
13868
  if (storyboardId !== this.currentStoryboardId) return;
13856
13869
  narrationStore.getState().setEngineStatus("executing");
13857
13870
  }, 0);
13858
- this.pendingTimers.add(markExecuting);
13871
+ this.pendingStepTimers.add(markExecuting);
13859
13872
  const last = steps[steps.length - 1];
13860
13873
  if (last) {
13861
13874
  const totalMs = last.at_ms + last.duration_ms;
@@ -13863,18 +13876,29 @@ var StoryboardEngine = class {
13863
13876
  if (storyboardId !== this.currentStoryboardId) return;
13864
13877
  narrationStore.getState().setEngineStatus("idle");
13865
13878
  }, totalMs + 50);
13866
- this.pendingTimers.add(markIdle);
13879
+ this.pendingStepTimers.add(markIdle);
13867
13880
  }
13868
13881
  }
13869
- /** Abort all pending steps and set engine status to idle. */
13882
+ /**
13883
+ * Abort pending STEP dispatches from the current storyboard. Overlay
13884
+ * removal timers are left alone so already-visible overlays still auto-
13885
+ * expire on their own schedule. To force-clear every overlay, call
13886
+ * `resetVisuals()` instead.
13887
+ */
13870
13888
  cancelPending() {
13871
- for (const t of this.pendingTimers) clearTimeout(t);
13872
- this.pendingTimers.clear();
13889
+ for (const t of this.pendingStepTimers) clearTimeout(t);
13890
+ this.pendingStepTimers.clear();
13873
13891
  this.deps.narrationStore.getState().setEngineStatus("idle");
13874
13892
  }
13875
- /** Reset visuals: clear overlays, fit camera back to page. */
13893
+ /** Cancel every removal timer (used by resetVisuals only). */
13894
+ cancelAllRemovalTimers() {
13895
+ for (const t of this.overlayRemovalTimers.values()) clearTimeout(t);
13896
+ this.overlayRemovalTimers.clear();
13897
+ }
13898
+ /** Reset visuals: clear overlays, cancel every removal timer, fit camera. */
13876
13899
  resetVisuals() {
13877
13900
  this.cancelPending();
13901
+ this.cancelAllRemovalTimers();
13878
13902
  const { narrationStore, bboxIndex, getViewport } = this.deps;
13879
13903
  narrationStore.getState().clearOverlays();
13880
13904
  const viewport = getViewport();
@@ -13945,8 +13969,9 @@ var StoryboardEngine = class {
13945
13969
  narrationStore.getState().addOverlay(overlay);
13946
13970
  const timer = setTimeout(() => {
13947
13971
  narrationStore.getState().removeOverlay(overlay.id);
13972
+ this.overlayRemovalTimers.delete(overlay.id);
13948
13973
  }, visibleMs);
13949
- this.pendingTimers.add(timer);
13974
+ this.overlayRemovalTimers.set(overlay.id, timer);
13950
13975
  return true;
13951
13976
  }
13952
13977
  applyCamera(action, durationMs) {
@@ -14997,6 +15022,8 @@ function TutorModeContainer({
14997
15022
  showExitButton = true,
14998
15023
  onExitTutorMode,
14999
15024
  minOverlayDurationMs,
15025
+ backgroundColor = "#ffffff",
15026
+ loadingComponent,
15000
15027
  className
15001
15028
  }) {
15002
15029
  const containerRef = (0, import_react56.useRef)(null);
@@ -15161,6 +15188,7 @@ function TutorModeContainer({
15161
15188
  const rasterScale = dpiScale * (scale || 1);
15162
15189
  const baseW = page ? page.page_dimensions.width * (scale || 1) : 0;
15163
15190
  const baseH = page ? page.page_dimensions.height * (scale || 1) : 0;
15191
+ const isReady = !!page && !!pageProxy;
15164
15192
  return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
15165
15193
  "div",
15166
15194
  {
@@ -15171,12 +15199,12 @@ function TutorModeContainer({
15171
15199
  width: "100%",
15172
15200
  height: "100%",
15173
15201
  overflow: "hidden",
15174
- background: "#111"
15202
+ background: backgroundColor
15175
15203
  },
15176
15204
  "data-role": "tutor-mode-container",
15177
- "data-page-loaded": page ? "true" : "false",
15205
+ "data-page-loaded": isReady ? "true" : "false",
15178
15206
  children: [
15179
- showExitButton ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
15207
+ showExitButton && isReady ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
15180
15208
  "button",
15181
15209
  {
15182
15210
  onClick: () => {
@@ -15193,7 +15221,9 @@ function TutorModeContainer({
15193
15221
  padding: "8px 14px",
15194
15222
  border: "none",
15195
15223
  borderRadius: 8,
15196
- background: "rgba(255,255,255,0.12)",
15224
+ // Dark translucent pill with white text reads cleanly on both
15225
+ // light and dark container backgrounds.
15226
+ background: "rgba(17,24,39,0.72)",
15197
15227
  color: "white",
15198
15228
  cursor: "pointer",
15199
15229
  fontFamily: "system-ui, sans-serif",
@@ -15205,7 +15235,7 @@ function TutorModeContainer({
15205
15235
  children: "Reset view"
15206
15236
  }
15207
15237
  ) : null,
15208
- page ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(CameraView, { camera, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
15238
+ isReady ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(CameraView, { camera, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
15209
15239
  "div",
15210
15240
  {
15211
15241
  style: {
@@ -15240,12 +15270,73 @@ function TutorModeContainer({
15240
15270
  )
15241
15271
  ]
15242
15272
  }
15243
- ) }) : null,
15273
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(TutorLoadingState, { custom: loadingComponent }),
15244
15274
  showSubtitles ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(SubtitleBar, { text: currentChunk ?? null }) : null
15245
15275
  ]
15246
15276
  }
15247
15277
  );
15248
15278
  }
15279
+ function TutorLoadingState({
15280
+ custom
15281
+ }) {
15282
+ if (custom) {
15283
+ return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
15284
+ "div",
15285
+ {
15286
+ style: {
15287
+ position: "absolute",
15288
+ inset: 0,
15289
+ display: "flex",
15290
+ alignItems: "center",
15291
+ justifyContent: "center"
15292
+ },
15293
+ "data-role": "tutor-loading",
15294
+ children: custom
15295
+ }
15296
+ );
15297
+ }
15298
+ return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
15299
+ "div",
15300
+ {
15301
+ style: {
15302
+ position: "absolute",
15303
+ inset: 0,
15304
+ display: "flex",
15305
+ flexDirection: "column",
15306
+ alignItems: "center",
15307
+ justifyContent: "center",
15308
+ gap: 12,
15309
+ color: "rgba(0,0,0,0.55)",
15310
+ fontFamily: "system-ui, sans-serif",
15311
+ fontSize: 13
15312
+ },
15313
+ "data-role": "tutor-loading",
15314
+ children: [
15315
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
15316
+ "div",
15317
+ {
15318
+ "aria-hidden": true,
15319
+ style: {
15320
+ width: 36,
15321
+ height: 36,
15322
+ borderRadius: "50%",
15323
+ border: "3px solid rgba(0,0,0,0.1)",
15324
+ borderTopColor: "rgba(0,0,0,0.45)",
15325
+ animation: "pdf-tutor-spin 0.9s linear infinite"
15326
+ }
15327
+ }
15328
+ ),
15329
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { children: "Loading document\u2026" }),
15330
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("style", { children: `
15331
+ @keyframes pdf-tutor-spin {
15332
+ from { transform: rotate(0deg); }
15333
+ to { transform: rotate(360deg); }
15334
+ }
15335
+ ` })
15336
+ ]
15337
+ }
15338
+ );
15339
+ }
15249
15340
 
15250
15341
  // src/director/transformers-embedding.ts
15251
15342
  var loaded = null;