@industry-theme/file-city-panel 0.2.80 → 0.3.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/dist/components/TourPlayer.d.ts +11 -0
- package/dist/components/TourPlayer.d.ts.map +1 -1
- package/dist/components/TourPlayer.stories.d.ts +15 -0
- package/dist/components/TourPlayer.stories.d.ts.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/mocks/MockTTSAdapter.d.ts +85 -0
- package/dist/mocks/MockTTSAdapter.d.ts.map +1 -0
- package/dist/panels.bundle.js +461 -1
- package/dist/panels.bundle.js.map +1 -1
- package/dist/types/TextToSpeech.d.ts +116 -0
- package/dist/types/TextToSpeech.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/panels.bundle.js
CHANGED
|
@@ -50650,13 +50650,22 @@ const TourPlayer = ({
|
|
|
50650
50650
|
onInteractiveAction,
|
|
50651
50651
|
visible = true,
|
|
50652
50652
|
position,
|
|
50653
|
-
skipWelcome = false
|
|
50653
|
+
skipWelcome = false,
|
|
50654
|
+
ttsAdapter,
|
|
50655
|
+
tourAudioContext,
|
|
50656
|
+
autoPlayAudio = false,
|
|
50657
|
+
autoAdvanceOnAudioEnd = false,
|
|
50658
|
+
autoAdvanceDelay = 1e3
|
|
50654
50659
|
}) => {
|
|
50655
50660
|
const { theme: theme2 } = useTheme();
|
|
50656
50661
|
const [internalStepIndex, setInternalStepIndex] = useState(0);
|
|
50657
50662
|
const [completedSteps, setCompletedSteps] = useState(/* @__PURE__ */ new Set());
|
|
50658
50663
|
const [isPlaying, setIsPlaying] = useState(skipWelcome);
|
|
50659
50664
|
const [autoAdvanceTimeRemaining, setAutoAdvanceTimeRemaining] = useState(null);
|
|
50665
|
+
const [ttsState, setTtsState] = useState((ttsAdapter == null ? void 0 : ttsAdapter.state) || null);
|
|
50666
|
+
const [audioReady, setAudioReady] = useState(false);
|
|
50667
|
+
const [isAutoPlaying, setIsAutoPlaying] = useState(false);
|
|
50668
|
+
const ttsStateUpdateInterval = useRef(null);
|
|
50660
50669
|
const currentStepIndex = controlledStepIndex !== void 0 ? controlledStepIndex : internalStepIndex;
|
|
50661
50670
|
const currentStep = tour.steps[currentStepIndex];
|
|
50662
50671
|
const isFirstStep = currentStepIndex === 0;
|
|
@@ -50722,6 +50731,100 @@ const TourPlayer = ({
|
|
|
50722
50731
|
},
|
|
50723
50732
|
[onInteractiveAction]
|
|
50724
50733
|
);
|
|
50734
|
+
useEffect(() => {
|
|
50735
|
+
if (!ttsAdapter || !tourAudioContext) return;
|
|
50736
|
+
async function loadAudio() {
|
|
50737
|
+
try {
|
|
50738
|
+
const stepIds = tour.steps.map((step) => step.id);
|
|
50739
|
+
await ttsAdapter.fetchTourAudio(tour.id, stepIds, tourAudioContext);
|
|
50740
|
+
setAudioReady(true);
|
|
50741
|
+
} catch (error) {
|
|
50742
|
+
console.error("[TourPlayer] Failed to load tour audio:", error);
|
|
50743
|
+
}
|
|
50744
|
+
}
|
|
50745
|
+
loadAudio();
|
|
50746
|
+
}, [ttsAdapter, tourAudioContext, tour]);
|
|
50747
|
+
useEffect(() => {
|
|
50748
|
+
if (!ttsAdapter) return;
|
|
50749
|
+
ttsStateUpdateInterval.current = setInterval(() => {
|
|
50750
|
+
setTtsState({ ...ttsAdapter.state });
|
|
50751
|
+
}, 100);
|
|
50752
|
+
return () => {
|
|
50753
|
+
if (ttsStateUpdateInterval.current) {
|
|
50754
|
+
clearInterval(ttsStateUpdateInterval.current);
|
|
50755
|
+
ttsStateUpdateInterval.current = null;
|
|
50756
|
+
}
|
|
50757
|
+
};
|
|
50758
|
+
}, [ttsAdapter]);
|
|
50759
|
+
useEffect(() => {
|
|
50760
|
+
if (!ttsAdapter || !autoAdvanceOnAudioEnd || !isAutoPlaying) return;
|
|
50761
|
+
const handleAudioEnded = () => {
|
|
50762
|
+
console.log("[TourPlayer] Audio ended, auto-advancing...");
|
|
50763
|
+
setTimeout(() => {
|
|
50764
|
+
if (!isLastStep) {
|
|
50765
|
+
nextStep();
|
|
50766
|
+
} else {
|
|
50767
|
+
console.log("[TourPlayer] Tour complete");
|
|
50768
|
+
setIsAutoPlaying(false);
|
|
50769
|
+
handleExit();
|
|
50770
|
+
}
|
|
50771
|
+
}, autoAdvanceDelay);
|
|
50772
|
+
};
|
|
50773
|
+
ttsAdapter.addEventListener("ended", handleAudioEnded);
|
|
50774
|
+
return () => ttsAdapter.removeEventListener("ended", handleAudioEnded);
|
|
50775
|
+
}, [ttsAdapter, autoAdvanceOnAudioEnd, isAutoPlaying, isLastStep, autoAdvanceDelay, nextStep, handleExit]);
|
|
50776
|
+
useEffect(() => {
|
|
50777
|
+
if (!ttsAdapter || !autoPlayAudio || !isPlaying || !audioReady || !isAutoPlaying) return;
|
|
50778
|
+
const playCurrentStep = async () => {
|
|
50779
|
+
try {
|
|
50780
|
+
await ttsAdapter.speak(currentStep.id);
|
|
50781
|
+
} catch (error) {
|
|
50782
|
+
console.error("[TourPlayer] Auto-play failed:", error);
|
|
50783
|
+
setIsAutoPlaying(false);
|
|
50784
|
+
}
|
|
50785
|
+
};
|
|
50786
|
+
playCurrentStep();
|
|
50787
|
+
}, [ttsAdapter, autoPlayAudio, currentStep == null ? void 0 : currentStep.id, isPlaying, audioReady, isAutoPlaying]);
|
|
50788
|
+
useEffect(() => {
|
|
50789
|
+
return () => {
|
|
50790
|
+
if (ttsAdapter) {
|
|
50791
|
+
ttsAdapter.stop();
|
|
50792
|
+
}
|
|
50793
|
+
};
|
|
50794
|
+
}, [ttsAdapter]);
|
|
50795
|
+
const handleReadAloud = useCallback(async () => {
|
|
50796
|
+
if (!ttsAdapter || !audioReady) return;
|
|
50797
|
+
if (ttsState == null ? void 0 : ttsState.isPlaying) {
|
|
50798
|
+
ttsAdapter.stop();
|
|
50799
|
+
return;
|
|
50800
|
+
}
|
|
50801
|
+
if (ttsState == null ? void 0 : ttsState.isPaused) {
|
|
50802
|
+
ttsAdapter.resume();
|
|
50803
|
+
return;
|
|
50804
|
+
}
|
|
50805
|
+
try {
|
|
50806
|
+
await ttsAdapter.speak(currentStep.id);
|
|
50807
|
+
} catch (error) {
|
|
50808
|
+
console.error("[TourPlayer] TTS failed:", error);
|
|
50809
|
+
}
|
|
50810
|
+
}, [ttsAdapter, ttsState, audioReady, currentStep == null ? void 0 : currentStep.id]);
|
|
50811
|
+
const toggleAutoPlay = useCallback(() => {
|
|
50812
|
+
if (!ttsAdapter || !audioReady) return;
|
|
50813
|
+
if (isAutoPlaying) {
|
|
50814
|
+
setIsAutoPlaying(false);
|
|
50815
|
+
ttsAdapter.stop();
|
|
50816
|
+
} else {
|
|
50817
|
+
setIsAutoPlaying(true);
|
|
50818
|
+
ttsAdapter.speak(currentStep.id);
|
|
50819
|
+
}
|
|
50820
|
+
}, [isAutoPlaying, ttsAdapter, audioReady, currentStep == null ? void 0 : currentStep.id]);
|
|
50821
|
+
const formatTime = (seconds) => {
|
|
50822
|
+
const mins = Math.floor(seconds / 60);
|
|
50823
|
+
const secs = Math.floor(seconds % 60);
|
|
50824
|
+
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
50825
|
+
};
|
|
50826
|
+
const showTTSControls = !!ttsAdapter && audioReady;
|
|
50827
|
+
const canAutoPlay = autoPlayAudio && autoAdvanceOnAudioEnd;
|
|
50725
50828
|
if (!visible) return null;
|
|
50726
50829
|
const useAbsolutePositioning = position === "top" || position === "overlay";
|
|
50727
50830
|
const absoluteContainerStyle = {
|
|
@@ -50980,6 +51083,40 @@ const TourPlayer = ({
|
|
|
50980
51083
|
"Start Tour"
|
|
50981
51084
|
]
|
|
50982
51085
|
}
|
|
51086
|
+
),
|
|
51087
|
+
showTTSControls && canAutoPlay && /* @__PURE__ */ jsxs(
|
|
51088
|
+
"button",
|
|
51089
|
+
{
|
|
51090
|
+
onClick: () => {
|
|
51091
|
+
handleStart();
|
|
51092
|
+
setIsAutoPlaying(true);
|
|
51093
|
+
},
|
|
51094
|
+
style: {
|
|
51095
|
+
display: "flex",
|
|
51096
|
+
alignItems: "center",
|
|
51097
|
+
gap: "6px",
|
|
51098
|
+
padding: "8px 16px",
|
|
51099
|
+
backgroundColor: theme2.colors.accent,
|
|
51100
|
+
color: "#ffffff",
|
|
51101
|
+
border: "none",
|
|
51102
|
+
borderRadius: "6px",
|
|
51103
|
+
fontSize: theme2.fontSizes[1],
|
|
51104
|
+
fontFamily: theme2.fonts.body,
|
|
51105
|
+
fontWeight: 600,
|
|
51106
|
+
cursor: "pointer",
|
|
51107
|
+
transition: "all 0.2s"
|
|
51108
|
+
},
|
|
51109
|
+
onMouseEnter: (e) => {
|
|
51110
|
+
e.currentTarget.style.backgroundColor = theme2.colors.accent + "dd";
|
|
51111
|
+
},
|
|
51112
|
+
onMouseLeave: (e) => {
|
|
51113
|
+
e.currentTarget.style.backgroundColor = theme2.colors.accent;
|
|
51114
|
+
},
|
|
51115
|
+
children: [
|
|
51116
|
+
/* @__PURE__ */ jsx(Volume2, { size: 16 }),
|
|
51117
|
+
"Start with Auto-Play"
|
|
51118
|
+
]
|
|
51119
|
+
}
|
|
50983
51120
|
)
|
|
50984
51121
|
]
|
|
50985
51122
|
}
|
|
@@ -51276,6 +51413,121 @@ const TourPlayer = ({
|
|
|
51276
51413
|
] })
|
|
51277
51414
|
}
|
|
51278
51415
|
),
|
|
51416
|
+
showTTSControls && /* @__PURE__ */ jsxs(
|
|
51417
|
+
"div",
|
|
51418
|
+
{
|
|
51419
|
+
style: {
|
|
51420
|
+
padding: "12px 16px",
|
|
51421
|
+
borderTop: `1px solid ${theme2.colors.border}`,
|
|
51422
|
+
flexShrink: 0,
|
|
51423
|
+
display: "flex",
|
|
51424
|
+
flexDirection: "column",
|
|
51425
|
+
gap: "8px"
|
|
51426
|
+
},
|
|
51427
|
+
children: [
|
|
51428
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
|
|
51429
|
+
/* @__PURE__ */ jsxs(
|
|
51430
|
+
"button",
|
|
51431
|
+
{
|
|
51432
|
+
onClick: handleReadAloud,
|
|
51433
|
+
disabled: (ttsState == null ? void 0 : ttsState.isLoading) || !audioReady,
|
|
51434
|
+
style: {
|
|
51435
|
+
display: "flex",
|
|
51436
|
+
alignItems: "center",
|
|
51437
|
+
gap: "6px",
|
|
51438
|
+
padding: "8px 12px",
|
|
51439
|
+
backgroundColor: "transparent",
|
|
51440
|
+
color: theme2.colors.text,
|
|
51441
|
+
border: `1px solid ${theme2.colors.border}`,
|
|
51442
|
+
borderRadius: "8px",
|
|
51443
|
+
fontSize: theme2.fontSizes[1],
|
|
51444
|
+
fontFamily: theme2.fonts.body,
|
|
51445
|
+
cursor: (ttsState == null ? void 0 : ttsState.isLoading) || !audioReady ? "not-allowed" : "pointer",
|
|
51446
|
+
opacity: (ttsState == null ? void 0 : ttsState.isLoading) || !audioReady ? 0.5 : 1,
|
|
51447
|
+
transition: "all 0.2s"
|
|
51448
|
+
},
|
|
51449
|
+
onMouseEnter: (e) => {
|
|
51450
|
+
if (!(ttsState == null ? void 0 : ttsState.isLoading) && audioReady) {
|
|
51451
|
+
e.currentTarget.style.backgroundColor = theme2.colors.backgroundLight;
|
|
51452
|
+
}
|
|
51453
|
+
},
|
|
51454
|
+
onMouseLeave: (e) => {
|
|
51455
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
51456
|
+
},
|
|
51457
|
+
children: [
|
|
51458
|
+
(ttsState == null ? void 0 : ttsState.isLoading) && /* @__PURE__ */ jsx(LoaderCircle, { size: 16, className: "animate-spin", style: { animation: "spin 1s linear infinite" } }),
|
|
51459
|
+
(ttsState == null ? void 0 : ttsState.isPlaying) && /* @__PURE__ */ jsx(Volume2, { size: 16 }),
|
|
51460
|
+
(ttsState == null ? void 0 : ttsState.isPaused) && /* @__PURE__ */ jsx(VolumeX, { size: 16 }),
|
|
51461
|
+
!(ttsState == null ? void 0 : ttsState.isPlaying) && !(ttsState == null ? void 0 : ttsState.isPaused) && !(ttsState == null ? void 0 : ttsState.isLoading) && /* @__PURE__ */ jsx(Volume2, { size: 16 }),
|
|
51462
|
+
/* @__PURE__ */ jsx("span", { children: (ttsState == null ? void 0 : ttsState.isLoading) ? "Loading..." : (ttsState == null ? void 0 : ttsState.isPlaying) ? "Stop" : (ttsState == null ? void 0 : ttsState.isPaused) ? "Resume" : "Read Aloud" })
|
|
51463
|
+
]
|
|
51464
|
+
}
|
|
51465
|
+
),
|
|
51466
|
+
canAutoPlay && /* @__PURE__ */ jsxs(
|
|
51467
|
+
"button",
|
|
51468
|
+
{
|
|
51469
|
+
onClick: toggleAutoPlay,
|
|
51470
|
+
disabled: !audioReady,
|
|
51471
|
+
style: {
|
|
51472
|
+
display: "flex",
|
|
51473
|
+
alignItems: "center",
|
|
51474
|
+
gap: "6px",
|
|
51475
|
+
padding: "8px 12px",
|
|
51476
|
+
backgroundColor: isAutoPlaying ? theme2.colors.primary : "transparent",
|
|
51477
|
+
color: isAutoPlaying ? "#ffffff" : theme2.colors.text,
|
|
51478
|
+
border: `1px solid ${isAutoPlaying ? theme2.colors.primary : theme2.colors.border}`,
|
|
51479
|
+
borderRadius: "8px",
|
|
51480
|
+
fontSize: theme2.fontSizes[1],
|
|
51481
|
+
fontFamily: theme2.fonts.body,
|
|
51482
|
+
cursor: !audioReady ? "not-allowed" : "pointer",
|
|
51483
|
+
opacity: !audioReady ? 0.5 : 1,
|
|
51484
|
+
transition: "all 0.2s"
|
|
51485
|
+
},
|
|
51486
|
+
onMouseEnter: (e) => {
|
|
51487
|
+
if (audioReady) {
|
|
51488
|
+
e.currentTarget.style.opacity = "0.8";
|
|
51489
|
+
}
|
|
51490
|
+
},
|
|
51491
|
+
onMouseLeave: (e) => {
|
|
51492
|
+
e.currentTarget.style.opacity = "1";
|
|
51493
|
+
},
|
|
51494
|
+
children: [
|
|
51495
|
+
isAutoPlaying ? /* @__PURE__ */ jsx(Pause, { size: 16 }) : /* @__PURE__ */ jsx(Play, { size: 16 }),
|
|
51496
|
+
/* @__PURE__ */ jsx("span", { children: isAutoPlaying ? "Stop Auto-Play" : "Start Auto-Play" })
|
|
51497
|
+
]
|
|
51498
|
+
}
|
|
51499
|
+
)
|
|
51500
|
+
] }),
|
|
51501
|
+
(ttsState == null ? void 0 : ttsState.isPlaying) && ttsState.duration && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
|
|
51502
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: theme2.fontSizes[0], color: theme2.colors.textSecondary, minWidth: "35px" }, children: formatTime(ttsState.currentTime || 0) }),
|
|
51503
|
+
/* @__PURE__ */ jsx(
|
|
51504
|
+
"div",
|
|
51505
|
+
{
|
|
51506
|
+
style: {
|
|
51507
|
+
flex: 1,
|
|
51508
|
+
height: "4px",
|
|
51509
|
+
backgroundColor: theme2.colors.backgroundLight,
|
|
51510
|
+
borderRadius: "2px",
|
|
51511
|
+
overflow: "hidden"
|
|
51512
|
+
},
|
|
51513
|
+
children: /* @__PURE__ */ jsx(
|
|
51514
|
+
"div",
|
|
51515
|
+
{
|
|
51516
|
+
style: {
|
|
51517
|
+
height: "100%",
|
|
51518
|
+
width: `${ttsState.progress || 0}%`,
|
|
51519
|
+
backgroundColor: theme2.colors.primary,
|
|
51520
|
+
transition: "width 0.1s linear"
|
|
51521
|
+
}
|
|
51522
|
+
}
|
|
51523
|
+
)
|
|
51524
|
+
}
|
|
51525
|
+
),
|
|
51526
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: theme2.fontSizes[0], color: theme2.colors.textSecondary, minWidth: "35px" }, children: formatTime(ttsState.duration) })
|
|
51527
|
+
] })
|
|
51528
|
+
]
|
|
51529
|
+
}
|
|
51530
|
+
),
|
|
51279
51531
|
/* @__PURE__ */ jsxs(
|
|
51280
51532
|
"div",
|
|
51281
51533
|
{
|
|
@@ -56068,6 +56320,212 @@ const ProjectInfoHeader = ({
|
|
|
56068
56320
|
}
|
|
56069
56321
|
);
|
|
56070
56322
|
};
|
|
56323
|
+
class MockTTSAdapter {
|
|
56324
|
+
constructor(config = {}) {
|
|
56325
|
+
this.audioCache = /* @__PURE__ */ new Map();
|
|
56326
|
+
this.eventTarget = new EventTarget();
|
|
56327
|
+
this.playbackTimer = null;
|
|
56328
|
+
this.progressTimer = null;
|
|
56329
|
+
this._state = {
|
|
56330
|
+
isLoading: false,
|
|
56331
|
+
isPlaying: false,
|
|
56332
|
+
isPaused: false,
|
|
56333
|
+
error: null
|
|
56334
|
+
};
|
|
56335
|
+
this.config = {
|
|
56336
|
+
audioDuration: config.audioDuration ?? 5,
|
|
56337
|
+
playbackDelay: config.playbackDelay ?? 100,
|
|
56338
|
+
fetchDelay: config.fetchDelay ?? 500,
|
|
56339
|
+
verbose: config.verbose ?? true,
|
|
56340
|
+
simulateError: config.simulateError ?? false
|
|
56341
|
+
};
|
|
56342
|
+
}
|
|
56343
|
+
get state() {
|
|
56344
|
+
return { ...this._state };
|
|
56345
|
+
}
|
|
56346
|
+
setState(updates) {
|
|
56347
|
+
this._state = { ...this._state, ...updates };
|
|
56348
|
+
}
|
|
56349
|
+
log(...args) {
|
|
56350
|
+
if (this.config.verbose) {
|
|
56351
|
+
console.log("[Mock TTS]", ...args);
|
|
56352
|
+
}
|
|
56353
|
+
}
|
|
56354
|
+
/**
|
|
56355
|
+
* Fetch audio URLs for all steps in a tour
|
|
56356
|
+
*/
|
|
56357
|
+
async fetchTourAudio(tourId, stepIds, context, options) {
|
|
56358
|
+
this.log("Fetching tour audio:", { tourId, stepCount: stepIds.length, context, options });
|
|
56359
|
+
this.setState({ isLoading: true, error: null });
|
|
56360
|
+
this.emit("loading");
|
|
56361
|
+
await new Promise((resolve) => setTimeout(resolve, this.config.fetchDelay));
|
|
56362
|
+
if (this.config.simulateError) {
|
|
56363
|
+
const error = new Error("Mock TTS: Simulated fetch error");
|
|
56364
|
+
this.setState({ isLoading: false, error });
|
|
56365
|
+
this.emit("error");
|
|
56366
|
+
throw error;
|
|
56367
|
+
}
|
|
56368
|
+
this.audioCache.clear();
|
|
56369
|
+
for (const stepId of stepIds) {
|
|
56370
|
+
const mockUrl = `mock://audio/${tourId}/${stepId}.mp3`;
|
|
56371
|
+
this.audioCache.set(stepId, mockUrl);
|
|
56372
|
+
}
|
|
56373
|
+
this.setState({ isLoading: false });
|
|
56374
|
+
this.log(`Loaded ${this.audioCache.size} audio files`);
|
|
56375
|
+
return new Map(this.audioCache);
|
|
56376
|
+
}
|
|
56377
|
+
/**
|
|
56378
|
+
* Play audio for a specific step
|
|
56379
|
+
*/
|
|
56380
|
+
async speak(stepId) {
|
|
56381
|
+
const audioUrl = this.audioCache.get(stepId);
|
|
56382
|
+
if (!audioUrl) {
|
|
56383
|
+
const error = new Error(`Mock TTS: No audio URL for step: ${stepId}`);
|
|
56384
|
+
this.setState({ error });
|
|
56385
|
+
this.emit("error");
|
|
56386
|
+
throw error;
|
|
56387
|
+
}
|
|
56388
|
+
this.log("Speaking step:", stepId);
|
|
56389
|
+
this.stop();
|
|
56390
|
+
if (this.config.simulateError) {
|
|
56391
|
+
const error = new Error("Mock TTS: Simulated playback error");
|
|
56392
|
+
this.setState({ error });
|
|
56393
|
+
this.emit("error");
|
|
56394
|
+
throw error;
|
|
56395
|
+
}
|
|
56396
|
+
await new Promise((resolve) => setTimeout(resolve, this.config.playbackDelay));
|
|
56397
|
+
this.setState({
|
|
56398
|
+
isPlaying: true,
|
|
56399
|
+
isPaused: false,
|
|
56400
|
+
error: null,
|
|
56401
|
+
duration: this.config.audioDuration,
|
|
56402
|
+
currentTime: 0,
|
|
56403
|
+
progress: 0
|
|
56404
|
+
});
|
|
56405
|
+
this.emit("playing");
|
|
56406
|
+
const startTime = Date.now();
|
|
56407
|
+
this.progressTimer = setInterval(() => {
|
|
56408
|
+
const elapsed = (Date.now() - startTime) / 1e3;
|
|
56409
|
+
const progress = Math.min(elapsed / this.config.audioDuration * 100, 100);
|
|
56410
|
+
this.setState({
|
|
56411
|
+
currentTime: elapsed,
|
|
56412
|
+
progress
|
|
56413
|
+
});
|
|
56414
|
+
}, 100);
|
|
56415
|
+
this.playbackTimer = setTimeout(() => {
|
|
56416
|
+
this.log("Playback ended for step:", stepId);
|
|
56417
|
+
this.cleanupPlayback();
|
|
56418
|
+
this.setState({
|
|
56419
|
+
isPlaying: false,
|
|
56420
|
+
isPaused: false,
|
|
56421
|
+
currentTime: 0,
|
|
56422
|
+
progress: 0
|
|
56423
|
+
});
|
|
56424
|
+
this.emit("ended");
|
|
56425
|
+
}, this.config.audioDuration * 1e3);
|
|
56426
|
+
}
|
|
56427
|
+
/**
|
|
56428
|
+
* Stop current audio playback
|
|
56429
|
+
*/
|
|
56430
|
+
stop() {
|
|
56431
|
+
this.log("Stopping playback");
|
|
56432
|
+
this.cleanupPlayback();
|
|
56433
|
+
this.setState({
|
|
56434
|
+
isPlaying: false,
|
|
56435
|
+
isPaused: false,
|
|
56436
|
+
currentTime: 0,
|
|
56437
|
+
progress: 0
|
|
56438
|
+
});
|
|
56439
|
+
}
|
|
56440
|
+
/**
|
|
56441
|
+
* Pause current audio playback
|
|
56442
|
+
*/
|
|
56443
|
+
pause() {
|
|
56444
|
+
if (this._state.isPlaying) {
|
|
56445
|
+
this.log("Pausing playback");
|
|
56446
|
+
this.cleanupPlayback();
|
|
56447
|
+
this.setState({ isPlaying: false, isPaused: true });
|
|
56448
|
+
this.emit("paused");
|
|
56449
|
+
}
|
|
56450
|
+
}
|
|
56451
|
+
/**
|
|
56452
|
+
* Resume paused audio playback
|
|
56453
|
+
*/
|
|
56454
|
+
resume() {
|
|
56455
|
+
if (this._state.isPaused && this._state.duration) {
|
|
56456
|
+
this.log("Resuming playback");
|
|
56457
|
+
const remainingTime = this._state.duration - (this._state.currentTime || 0);
|
|
56458
|
+
const startTime = Date.now() - (this._state.currentTime || 0) * 1e3;
|
|
56459
|
+
this.setState({ isPlaying: true, isPaused: false });
|
|
56460
|
+
this.emit("playing");
|
|
56461
|
+
this.progressTimer = setInterval(() => {
|
|
56462
|
+
const elapsed = (Date.now() - startTime) / 1e3;
|
|
56463
|
+
const progress = Math.min(elapsed / this._state.duration * 100, 100);
|
|
56464
|
+
this.setState({
|
|
56465
|
+
currentTime: elapsed,
|
|
56466
|
+
progress
|
|
56467
|
+
});
|
|
56468
|
+
}, 100);
|
|
56469
|
+
this.playbackTimer = setTimeout(() => {
|
|
56470
|
+
this.log("Playback ended (resumed)");
|
|
56471
|
+
this.cleanupPlayback();
|
|
56472
|
+
this.setState({
|
|
56473
|
+
isPlaying: false,
|
|
56474
|
+
isPaused: false,
|
|
56475
|
+
currentTime: 0,
|
|
56476
|
+
progress: 0
|
|
56477
|
+
});
|
|
56478
|
+
this.emit("ended");
|
|
56479
|
+
}, remainingTime * 1e3);
|
|
56480
|
+
}
|
|
56481
|
+
}
|
|
56482
|
+
/**
|
|
56483
|
+
* Subscribe to TTS events
|
|
56484
|
+
*/
|
|
56485
|
+
addEventListener(event, handler) {
|
|
56486
|
+
this.eventTarget.addEventListener(event, handler);
|
|
56487
|
+
}
|
|
56488
|
+
/**
|
|
56489
|
+
* Unsubscribe from TTS events
|
|
56490
|
+
*/
|
|
56491
|
+
removeEventListener(event, handler) {
|
|
56492
|
+
this.eventTarget.removeEventListener(event, handler);
|
|
56493
|
+
}
|
|
56494
|
+
/**
|
|
56495
|
+
* Emit an event
|
|
56496
|
+
*/
|
|
56497
|
+
emit(event) {
|
|
56498
|
+
this.eventTarget.dispatchEvent(new Event(event));
|
|
56499
|
+
}
|
|
56500
|
+
/**
|
|
56501
|
+
* Clean up timers
|
|
56502
|
+
*/
|
|
56503
|
+
cleanupPlayback() {
|
|
56504
|
+
if (this.playbackTimer) {
|
|
56505
|
+
clearTimeout(this.playbackTimer);
|
|
56506
|
+
this.playbackTimer = null;
|
|
56507
|
+
}
|
|
56508
|
+
if (this.progressTimer) {
|
|
56509
|
+
clearInterval(this.progressTimer);
|
|
56510
|
+
this.progressTimer = null;
|
|
56511
|
+
}
|
|
56512
|
+
}
|
|
56513
|
+
/**
|
|
56514
|
+
* Clear all cached audio URLs
|
|
56515
|
+
*/
|
|
56516
|
+
clearCache() {
|
|
56517
|
+
this.audioCache.clear();
|
|
56518
|
+
}
|
|
56519
|
+
/**
|
|
56520
|
+
* Get cache statistics (for debugging)
|
|
56521
|
+
*/
|
|
56522
|
+
getCacheStats() {
|
|
56523
|
+
return {
|
|
56524
|
+
size: this.audioCache.size,
|
|
56525
|
+
entries: Array.from(this.audioCache.keys())
|
|
56526
|
+
};
|
|
56527
|
+
}
|
|
56528
|
+
}
|
|
56071
56529
|
const panels = [
|
|
56072
56530
|
{
|
|
56073
56531
|
metadata: {
|
|
@@ -56145,10 +56603,12 @@ export {
|
|
|
56145
56603
|
FeedProjectHeaderSkeleton,
|
|
56146
56604
|
FileCardList,
|
|
56147
56605
|
GitChangesCardList,
|
|
56606
|
+
MockTTSAdapter,
|
|
56148
56607
|
PrChangesCardList,
|
|
56149
56608
|
ProjectInfoHeader,
|
|
56150
56609
|
QUALITY_COLOR_MODES,
|
|
56151
56610
|
StoryboardFilesCardList,
|
|
56611
|
+
TourPlayer,
|
|
56152
56612
|
codeCityPanelTools,
|
|
56153
56613
|
codeCityPanelToolsMetadata,
|
|
56154
56614
|
focusBuildingTool,
|