@linto-ai/transcript-ui-core 0.9.18 → 0.9.20
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linto-ai/transcript-ui-core",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.20",
|
|
4
4
|
"description": "State layer and UI shell for @linto-ai/transcript-ui — core, stores, format adapters, and the TranscriptUI component",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vue",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"vue": "^3.5.0"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@linto-ai/transcript-ui-i18n": "0.9.
|
|
43
|
-
"@linto-ai/transcript-ui-ui": "0.9.
|
|
42
|
+
"@linto-ai/transcript-ui-i18n": "0.9.20",
|
|
43
|
+
"@linto-ai/transcript-ui-ui": "0.9.20",
|
|
44
44
|
"vue-stick-to-bottom": "^1.0.0"
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -176,15 +176,16 @@ onBeforeUnmount(() => {
|
|
|
176
176
|
</div>
|
|
177
177
|
|
|
178
178
|
<Transition name="fade-slide">
|
|
179
|
-
<
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
179
|
+
<div v-if="showResumeButton" class="resume-scroll-anchor">
|
|
180
|
+
<Button
|
|
181
|
+
size="sm"
|
|
182
|
+
icon="arrow-down"
|
|
183
|
+
class="resume-scroll-btn"
|
|
184
|
+
:aria-label="t('transcription.resumeScroll')"
|
|
185
|
+
@click="onResumeClick">
|
|
186
|
+
{{ t("transcription.resumeScroll") }}
|
|
187
|
+
</Button>
|
|
188
|
+
</div>
|
|
188
189
|
</Transition>
|
|
189
190
|
</div>
|
|
190
191
|
</article>
|
|
@@ -231,13 +232,22 @@ onBeforeUnmount(() => {
|
|
|
231
232
|
font-size: var(--font-size-sm);
|
|
232
233
|
}
|
|
233
234
|
|
|
234
|
-
/* Resume scroll button
|
|
235
|
-
.
|
|
235
|
+
/* Resume scroll button — placement lives on this wrapper, never on the
|
|
236
|
+
button itself: Button.vue's `.editor-btn` base rule resets `position` via
|
|
237
|
+
`all: unset` (and, in the webcomponent build, gets re-injected a second
|
|
238
|
+
time after everything else to reach teleported popovers/dialogs — see
|
|
239
|
+
packages/webcomponent/src/index.ts), so any positioning put directly on
|
|
240
|
+
the button is one rebuild away from being silently overridden again. */
|
|
241
|
+
.resume-scroll-anchor {
|
|
236
242
|
position: sticky;
|
|
237
243
|
bottom: var(--spacing-lg);
|
|
238
|
-
left: 50%;
|
|
239
|
-
translate: -50% 0;
|
|
240
244
|
z-index: var(--z-sticky);
|
|
245
|
+
display: flex;
|
|
246
|
+
flex-direction: column;
|
|
247
|
+
align-items: center;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.resume-scroll-btn {
|
|
241
251
|
/* No backdrop-filter: this button is sticky inside the tall scroll
|
|
242
252
|
container, where a backdrop-filter makes WebRender allocate a render
|
|
243
253
|
target spanning the whole scroll height — multi-GB on a long transcript. */
|
|
@@ -257,7 +267,7 @@ onBeforeUnmount(() => {
|
|
|
257
267
|
.fade-slide-enter-from,
|
|
258
268
|
.fade-slide-leave-to {
|
|
259
269
|
opacity: 0;
|
|
260
|
-
translate:
|
|
270
|
+
translate: 0 8px;
|
|
261
271
|
}
|
|
262
272
|
|
|
263
273
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -79,6 +79,13 @@ export function useFollowPlayback(
|
|
|
79
79
|
},
|
|
80
80
|
)
|
|
81
81
|
|
|
82
|
+
// Re-enable follow on any seek (widget scrub or click-to-read): the user
|
|
83
|
+
// just told the player where to be, which is exactly what follow means to
|
|
84
|
+
// track — symmetric with the isPlaying re-enable above.
|
|
85
|
+
const stopSeekListener = core.on("audio:seek", () => {
|
|
86
|
+
isFollowing.value = true
|
|
87
|
+
})
|
|
88
|
+
|
|
82
89
|
function onManualScroll() {
|
|
83
90
|
isFollowing.value = false
|
|
84
91
|
}
|
|
@@ -89,12 +96,14 @@ export function useFollowPlayback(
|
|
|
89
96
|
}
|
|
90
97
|
}
|
|
91
98
|
|
|
99
|
+
// wheel/touchstart/keydown are real scroll gestures — a bare pointerdown
|
|
100
|
+
// isn't (clicking a word, a button, anything in the panel triggers it
|
|
101
|
+
// without ever moving the scroll position), so it's deliberately excluded.
|
|
92
102
|
function setupScrollListener(handler: (e: Event) => void) {
|
|
93
103
|
const el = scrollContainer.value
|
|
94
104
|
if (!el) return
|
|
95
105
|
el.addEventListener("wheel", handler, { passive: true })
|
|
96
106
|
el.addEventListener("touchstart", handler, { passive: true })
|
|
97
|
-
el.addEventListener("pointerdown", handler, { passive: true })
|
|
98
107
|
el.addEventListener("keydown", checkKeyDownIsScroll)
|
|
99
108
|
}
|
|
100
109
|
|
|
@@ -103,7 +112,6 @@ export function useFollowPlayback(
|
|
|
103
112
|
if (!el) return
|
|
104
113
|
el.removeEventListener("wheel", handler)
|
|
105
114
|
el.removeEventListener("touchstart", handler)
|
|
106
|
-
el.removeEventListener("pointerdown", handler)
|
|
107
115
|
el.removeEventListener("keydown", checkKeyDownIsScroll)
|
|
108
116
|
}
|
|
109
117
|
|
|
@@ -113,6 +121,7 @@ export function useFollowPlayback(
|
|
|
113
121
|
|
|
114
122
|
onBeforeUnmount(() => {
|
|
115
123
|
downScrollListener(onManualScroll)
|
|
124
|
+
stopSeekListener()
|
|
116
125
|
})
|
|
117
126
|
|
|
118
127
|
function resumeFollow() {
|
package/src/core/types.ts
CHANGED
|
@@ -30,6 +30,10 @@ export interface CoreEventMap {
|
|
|
30
30
|
"speaker:add": { speaker: Speaker }
|
|
31
31
|
"speaker:remove": { speakerId: string }
|
|
32
32
|
"scroll:top": { translationId: string }
|
|
33
|
+
/** A seek was requested through core.audio.seekTo (widget scrub or
|
|
34
|
+
* click-to-read) — the explicit "take me to this instant" gesture that
|
|
35
|
+
* should resume follow-playback scrolling. */
|
|
36
|
+
"audio:seek": { time: number }
|
|
33
37
|
"translation:sync": { translationId: string }
|
|
34
38
|
"channel:sync": { channelId: string }
|
|
35
39
|
"channel:reset": { channelId: string }
|