@linto-ai/transcript-ui-core 0.9.14 → 0.9.16
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.16",
|
|
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.16",
|
|
43
|
+
"@linto-ai/transcript-ui-ui": "0.9.16",
|
|
44
44
|
"vue-stick-to-bottom": "^1.0.0"
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -12,7 +12,7 @@ import TranslationSelector from "./TranslationSelector.vue"
|
|
|
12
12
|
import SelectionActionBar from "./SelectionActionBar.vue"
|
|
13
13
|
import { useIsMobile } from "../composables/useIsMobile"
|
|
14
14
|
import { provideTurnSelection } from "../composables/useTurnSelection"
|
|
15
|
-
import { useCore } from "../core"
|
|
15
|
+
import { useCore, type LLMService } from "../core"
|
|
16
16
|
|
|
17
17
|
const props = withDefaults(
|
|
18
18
|
defineProps<{
|
|
@@ -27,7 +27,41 @@ const core = useCore()
|
|
|
27
27
|
const { isMobile } = useIsMobile()
|
|
28
28
|
const isSidebarOpen = ref(false)
|
|
29
29
|
|
|
30
|
-
const
|
|
30
|
+
const shownPanels = ref<string[]>([TRANSCRIPTION_TAB])
|
|
31
|
+
// The ?? never actually triggers — every shownPanels mutation keeps at least one entry — it's just satisfying noUncheckedIndexedAccess.
|
|
32
|
+
const activeTab = computed(() => shownPanels.value[0] ?? TRANSCRIPTION_TAB)
|
|
33
|
+
|
|
34
|
+
const isSplit = computed({
|
|
35
|
+
get: () => shownPanels.value.length > 1,
|
|
36
|
+
set: (value: boolean) => {
|
|
37
|
+
shownPanels.value = value
|
|
38
|
+
? [...shownPanels.value, VERBATIM_TAB]
|
|
39
|
+
: shownPanels.value.filter((id) => id !== VERBATIM_TAB)
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
type PanelDescriptor =
|
|
44
|
+
| { id: string; kind: "transcription" }
|
|
45
|
+
| { id: string; kind: "verbatim" }
|
|
46
|
+
| { id: string; kind: "service"; service: LLMService }
|
|
47
|
+
|
|
48
|
+
// Resolved once per render, not inline in the template: avoids calling
|
|
49
|
+
// core.llmServices.get() twice per item (once to guard, once to bind) with
|
|
50
|
+
// no type-narrowing between the two — a discriminated union the template
|
|
51
|
+
// just switches on instead.
|
|
52
|
+
const panels = computed<PanelDescriptor[]>(() =>
|
|
53
|
+
shownPanels.value.map((id): PanelDescriptor => {
|
|
54
|
+
if (id === TRANSCRIPTION_TAB) return { id, kind: "transcription" }
|
|
55
|
+
if (id === VERBATIM_TAB) return { id, kind: "verbatim" }
|
|
56
|
+
const service = core.llmServices?.get(id)
|
|
57
|
+
// Unknown/vanished service id (e.g. llmServices isn't installed):
|
|
58
|
+
// same fallback as the old single-tab code — show transcription rather
|
|
59
|
+
// than nothing.
|
|
60
|
+
return service
|
|
61
|
+
? { id, kind: "service", service }
|
|
62
|
+
: { id, kind: "transcription" }
|
|
63
|
+
}),
|
|
64
|
+
)
|
|
31
65
|
|
|
32
66
|
const activeTurns = computed(
|
|
33
67
|
() => core.activeChannel.value?.activeTranslation.value.turns.value ?? [],
|
|
@@ -46,11 +80,6 @@ const activeTranslationId = computed(
|
|
|
46
80
|
const speakerList = computed(() => Array.from(speakers.values()))
|
|
47
81
|
|
|
48
82
|
const showTranscription = computed(() => activeTab.value === TRANSCRIPTION_TAB)
|
|
49
|
-
const showVerbatim = computed(() => activeTab.value === VERBATIM_TAB)
|
|
50
|
-
const activeService = computed(() => {
|
|
51
|
-
if (showTranscription.value || showVerbatim.value) return null
|
|
52
|
-
return core.llmServices?.get(activeTab.value) ?? null
|
|
53
|
-
})
|
|
54
83
|
|
|
55
84
|
watch(activeTab, (id) => {
|
|
56
85
|
if (!core.llmServices) return
|
|
@@ -69,7 +98,7 @@ watch(
|
|
|
69
98
|
activeTab.value !== VERBATIM_TAB &&
|
|
70
99
|
!core.llmServices?.get(activeTab.value)
|
|
71
100
|
) {
|
|
72
|
-
|
|
101
|
+
shownPanels.value = [TRANSCRIPTION_TAB]
|
|
73
102
|
}
|
|
74
103
|
},
|
|
75
104
|
)
|
|
@@ -115,22 +144,31 @@ function onTranslationChange(translationId: string) {
|
|
|
115
144
|
@open-chat="core.chat?.setDrawerOpen(true)"
|
|
116
145
|
@undo="core.transcriptionEditor?.undo()"
|
|
117
146
|
@redo="core.transcriptionEditor?.redo()" />
|
|
118
|
-
<TabBar
|
|
147
|
+
<TabBar
|
|
148
|
+
:model-value="activeTab"
|
|
149
|
+
@update:model-value="(tab) => (shownPanels = [tab])" />
|
|
119
150
|
<SelectionActionBar v-if="showTranscription" />
|
|
120
|
-
<main
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
151
|
+
<main
|
|
152
|
+
class="editor-body"
|
|
153
|
+
:class="{ 'editor-body--no-sidebar': panels.length > 1 }">
|
|
154
|
+
<div
|
|
155
|
+
class="editor-body__panels"
|
|
156
|
+
:class="{ 'editor-body__panels--split': panels.length > 1 }">
|
|
157
|
+
<template v-for="panel in panels" :key="panel.id">
|
|
158
|
+
<TranscriptionPanel
|
|
159
|
+
v-if="panel.kind === 'transcription'"
|
|
160
|
+
:turns="activeTurns"
|
|
161
|
+
:speakers="speakers" />
|
|
162
|
+
<VerbatimPanel v-else-if="panel.kind === 'verbatim'" />
|
|
163
|
+
<component
|
|
164
|
+
:is="core.components.llmServicePanel"
|
|
165
|
+
v-else
|
|
166
|
+
:service="panel.service"
|
|
167
|
+
v-model:split="isSplit" />
|
|
168
|
+
</template>
|
|
169
|
+
</div>
|
|
132
170
|
<SpeakerSidebar
|
|
133
|
-
v-if="!isMobile"
|
|
171
|
+
v-if="!isMobile && panels.length === 1"
|
|
134
172
|
:speakers="speakerList"
|
|
135
173
|
:channels="channels"
|
|
136
174
|
:selected-channel-id="core.activeChannelId.value"
|
|
@@ -140,7 +178,9 @@ function onTranslationChange(translationId: string) {
|
|
|
140
178
|
@update:selected-channel-id="onChannelChange"
|
|
141
179
|
@update:selected-translation-id="onTranslationChange" />
|
|
142
180
|
|
|
143
|
-
<SidebarDrawer
|
|
181
|
+
<SidebarDrawer
|
|
182
|
+
v-if="isMobile && panels.length === 1"
|
|
183
|
+
v-model:open="isSidebarOpen">
|
|
144
184
|
<SpeakerSidebar
|
|
145
185
|
:speakers="speakerList"
|
|
146
186
|
:channels="channels"
|
|
@@ -201,6 +241,27 @@ function onTranslationChange(translationId: string) {
|
|
|
201
241
|
min-height: 0;
|
|
202
242
|
}
|
|
203
243
|
|
|
244
|
+
/* Split mode: two panels already share the body between them, no room (or
|
|
245
|
+
need) for the speaker sidebar too — see Layout's panels/isSplit state. */
|
|
246
|
+
.editor-body--no-sidebar {
|
|
247
|
+
grid-template-columns: 1fr;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.editor-body__panels {
|
|
251
|
+
display: flex;
|
|
252
|
+
min-width: 0;
|
|
253
|
+
min-height: 0;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.editor-body__panels > * {
|
|
257
|
+
flex: 1;
|
|
258
|
+
min-width: 0;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.editor-body__panels--split > * + * {
|
|
262
|
+
border-left: 1px solid var(--color-border);
|
|
263
|
+
}
|
|
264
|
+
|
|
204
265
|
.mobile-selectors {
|
|
205
266
|
display: flex;
|
|
206
267
|
gap: var(--spacing-sm);
|
|
@@ -55,7 +55,13 @@ defineExpose({ core })
|
|
|
55
55
|
</template>
|
|
56
56
|
|
|
57
57
|
<style lang="css">
|
|
58
|
-
|
|
58
|
+
/* NOT fonts.css: a @font-face declared inside a shadow root isn't reliably
|
|
59
|
+
* applied by browsers (document.fonts never registers it) — dead weight in
|
|
60
|
+
* the webcomponent bundle specifically, which always runs shadow-DOM'd. The
|
|
61
|
+
* host declares --font-family's actual font at document level instead (see
|
|
62
|
+
* variables.css's token doc). A direct, non-shadow-DOM Vue embedder of this
|
|
63
|
+
* package (no such limitation) can still opt in: `@import
|
|
64
|
+
* "@linto-ai/transcript-ui-ui/styles/fonts.css"` themselves. */
|
|
59
65
|
@import "@linto-ai/transcript-ui-ui/styles/variables.css";
|
|
60
66
|
@import "@linto-ai/transcript-ui-ui/styles/base.css";
|
|
61
67
|
@import "@linto-ai/transcript-ui-ui/styles/popover-list.css";
|
|
@@ -68,7 +68,9 @@ function onExport(format?: string): void {
|
|
|
68
68
|
<section class="verbatim-panel">
|
|
69
69
|
<DocumentArticle>
|
|
70
70
|
<template #toolbar-right>
|
|
71
|
-
<DownloadMenu
|
|
71
|
+
<DownloadMenu
|
|
72
|
+
:formats="core.verbatimFormats.value"
|
|
73
|
+
@select="onExport" />
|
|
72
74
|
</template>
|
|
73
75
|
|
|
74
76
|
<article class="verbatim-panel__content">
|
|
@@ -109,7 +111,7 @@ function onExport(format?: string): void {
|
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
.verbatim-panel__content {
|
|
112
|
-
padding:
|
|
114
|
+
padding: 4rem clamp(1.5rem, 6rem, 8%);
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
.verbatim-panel__header {
|
|
@@ -165,9 +167,4 @@ function onExport(format?: string): void {
|
|
|
165
167
|
color: var(--color-text-primary);
|
|
166
168
|
}
|
|
167
169
|
|
|
168
|
-
@media (max-width: 767px) {
|
|
169
|
-
.verbatim-panel {
|
|
170
|
-
padding: var(--spacing-md);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
170
|
</style>
|