@linto-ai/transcript-ui-core 0.9.0

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.
Files changed (101) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +46 -0
  3. package/package.json +46 -0
  4. package/src/adapters/apiAdapter.ts +44 -0
  5. package/src/adapters/mapApiTurns.ts +32 -0
  6. package/src/adapters/test/mapApiTurns.test.ts +73 -0
  7. package/src/adapters/whisperXAdapter.ts +73 -0
  8. package/src/components/ChannelSelector.vue +31 -0
  9. package/src/components/EditorErrorOverlay.vue +55 -0
  10. package/src/components/EditorLoadingOverlay.vue +52 -0
  11. package/src/components/Header.vue +138 -0
  12. package/src/components/Layout.vue +221 -0
  13. package/src/components/MicrophoneIllustration.vue +28 -0
  14. package/src/components/SelectionActionBar.vue +86 -0
  15. package/src/components/SidebarDrawer.vue +32 -0
  16. package/src/components/SpeakerLabel.vue +105 -0
  17. package/src/components/SpeakerSidebar.vue +455 -0
  18. package/src/components/TabBar.constants.ts +2 -0
  19. package/src/components/TabBar.vue +52 -0
  20. package/src/components/TranscriptUI.vue +68 -0
  21. package/src/components/TranscriptionEmpty.vue +39 -0
  22. package/src/components/TranscriptionPanel.vue +275 -0
  23. package/src/components/TranscriptionTurn.vue +407 -0
  24. package/src/components/TranslationSelector.vue +39 -0
  25. package/src/components/VerbatimPanel.vue +160 -0
  26. package/src/components/molecules/MergeDialog.vue +150 -0
  27. package/src/components/molecules/MergeTurnsButton.vue +57 -0
  28. package/src/components/molecules/SpeakerPopover.vue +146 -0
  29. package/src/composables/useEditorReady.ts +62 -0
  30. package/src/composables/useFollowPlayback.ts +124 -0
  31. package/src/composables/useIsMobile.ts +24 -0
  32. package/src/composables/useTurnSelection.ts +169 -0
  33. package/src/constants/speakers.ts +14 -0
  34. package/src/core/createCore.ts +162 -0
  35. package/src/core/helpers/addSpeaker.ts +11 -0
  36. package/src/core/helpers/countTurnsForSpeaker.ts +9 -0
  37. package/src/core/helpers/createSpeakerAndAssign.ts +16 -0
  38. package/src/core/helpers/ensureDocumentSpeakers.ts +26 -0
  39. package/src/core/helpers/ensureSpeaker.ts +12 -0
  40. package/src/core/helpers/ensureSpeakersFromTurns.ts +14 -0
  41. package/src/core/helpers/findTurnIndex.ts +5 -0
  42. package/src/core/helpers/index.ts +15 -0
  43. package/src/core/helpers/insertTurn.ts +5 -0
  44. package/src/core/helpers/mergeSpeakers.ts +29 -0
  45. package/src/core/helpers/patchTurn.ts +15 -0
  46. package/src/core/helpers/prependTurns.ts +5 -0
  47. package/src/core/helpers/removeTurn.ts +8 -0
  48. package/src/core/helpers/renameSpeaker.ts +12 -0
  49. package/src/core/helpers/speakerEquals.ts +6 -0
  50. package/src/core/helpers/switchTurnSpeaker.ts +17 -0
  51. package/src/core/helpers/test/countTurnsForSpeaker.test.ts +16 -0
  52. package/src/core/helpers/test/createSpeakerAndAssign.test.ts +22 -0
  53. package/src/core/helpers/test/makeTestCore.ts +40 -0
  54. package/src/core/helpers/test/mergeSpeakers.test.ts +24 -0
  55. package/src/core/helpers/test/renameSpeaker.test.ts +19 -0
  56. package/src/core/helpers/test/switchTurnSpeaker.test.ts +27 -0
  57. package/src/core/helpers/updateTurnWords.ts +22 -0
  58. package/src/core/index.ts +47 -0
  59. package/src/core/modules/eventBus.ts +43 -0
  60. package/src/core/stores/channelStore.ts +100 -0
  61. package/src/core/stores/crossTranslationStore.ts +141 -0
  62. package/src/core/stores/index.ts +7 -0
  63. package/src/core/stores/speakersStore.ts +53 -0
  64. package/src/core/stores/translationStore.ts +133 -0
  65. package/src/core/types.ts +602 -0
  66. package/src/core/useCore.ts +16 -0
  67. package/src/index.ts +88 -0
  68. package/src/types/api.ts +52 -0
  69. package/src/types/editor.ts +65 -0
  70. package/src/types/whisperx.ts +22 -0
  71. package/src/utils/color.ts +7 -0
  72. package/src/utils/computeCaretOffsetFromPoint.ts +56 -0
  73. package/src/utils/computeTurnPlainText.ts +9 -0
  74. package/src/utils/extractLangCode.ts +7 -0
  75. package/src/utils/findWordAtOffset.ts +16 -0
  76. package/src/utils/index.ts +19 -0
  77. package/src/utils/intl.ts +47 -0
  78. package/src/utils/isSameLanguage.ts +13 -0
  79. package/src/utils/test/computeTurnPlainText.test.ts +34 -0
  80. package/src/utils/test/findWordAtOffset.test.ts +25 -0
  81. package/src/utils/throttle.ts +23 -0
  82. package/src/utils/time.ts +95 -0
  83. package/src/utils/tokenize.ts +34 -0
  84. package/src/utils/tts.ts +49 -0
  85. package/src/utils/turnWords/carryWordTimes.ts +37 -0
  86. package/src/utils/turnWords/index.ts +6 -0
  87. package/src/utils/turnWords/layoutWords.ts +41 -0
  88. package/src/utils/turnWords/parseWordId.ts +13 -0
  89. package/src/utils/turnWords/test/carryWordTimes.test.ts +47 -0
  90. package/src/utils/turnWords/test/layoutWords.test.ts +54 -0
  91. package/src/utils/turnWords/test/parseWordId.test.ts +24 -0
  92. package/src/utils/turnWords/test/wordId.test.ts +9 -0
  93. package/src/utils/turnWords/test/wordsFromApi.test.ts +20 -0
  94. package/src/utils/turnWords/test/wordsFromText.test.ts +17 -0
  95. package/src/utils/turnWords/wordId.ts +9 -0
  96. package/src/utils/turnWords/wordsFromApi.ts +16 -0
  97. package/src/utils/turnWords/wordsFromText.ts +14 -0
  98. package/src/utils/validateDocument.ts +80 -0
  99. package/src/utils/waveform.ts +64 -0
  100. package/src/utils/wordRange.ts +67 -0
  101. package/src/utils/words.ts +45 -0
@@ -0,0 +1,133 @@
1
+ import { ref, shallowRef } from "vue"
2
+ import type { AudioSource, Turn, Word } from "../../types/editor"
3
+ import type { CoreEventMap, TranslationInfo, TranslationStore } from "../types"
4
+ import { insertTurn } from "../helpers/insertTurn"
5
+ import { prependTurns as prependTurnsHelper } from "../helpers/prependTurns"
6
+ import { patchTurn } from "../helpers/patchTurn"
7
+ import { removeTurn as removeTurnHelper } from "../helpers/removeTurn"
8
+ import { updateTurnWords } from "../helpers/updateTurnWords"
9
+ import { ensureSpeakersFromTurns } from "../helpers/ensureSpeakersFromTurns"
10
+
11
+ interface TranslationInit extends TranslationInfo {
12
+ audio?: AudioSource
13
+ turns: Turn[]
14
+ }
15
+
16
+ type Emit = <K extends keyof CoreEventMap>(
17
+ event: K,
18
+ payload: CoreEventMap[K],
19
+ ) => void
20
+ type SpeakersEnsure = (speakerId: string | null, name?: string) => void
21
+
22
+ export function createTranslationStore(
23
+ init: TranslationInit,
24
+ emit: Emit,
25
+ speakersEnsure: SpeakersEnsure,
26
+ ): TranslationStore {
27
+ const { id, languages, isSource, audio } = init
28
+ const turns = shallowRef<Turn[]>(init.turns)
29
+ const lastModifiedAt = ref<number | null>(null)
30
+
31
+ function setLastModifiedAt(ts: number | null): void {
32
+ lastModifiedAt.value = ts
33
+ }
34
+
35
+ // ── Index: turnId → array position (O(1) lookup) ─────────────────────
36
+ const indexMap = new Map<string, number>()
37
+
38
+ function rebuildIndex(): void {
39
+ indexMap.clear()
40
+ const arr = turns.value
41
+ for (let i = 0; i < arr.length; i++) {
42
+ indexMap.set(arr[i]!.id, i)
43
+ }
44
+ }
45
+ rebuildIndex()
46
+
47
+ function addTurn(turn: Turn): void {
48
+ speakersEnsure(turn.speakerId)
49
+ indexMap.set(turn.id, turns.value.length)
50
+ turns.value = insertTurn(turns.value, turn)
51
+ emit("turn:add", { turn, translationId: id })
52
+ }
53
+
54
+ function updateTurn(turnId: string, patch: Partial<Turn>): void {
55
+ const result = patchTurn(turns.value, turnId, patch)
56
+ if (!result) return
57
+ turns.value = result.turns
58
+ emit("turn:update", { turn: result.updated, translationId: id })
59
+ }
60
+
61
+ function removeTurn(turnId: string): void {
62
+ const result = removeTurnHelper(turns.value, turnId)
63
+ if (!result) return
64
+ turns.value = result
65
+ rebuildIndex()
66
+ emit("turn:remove", { turnId, translationId: id })
67
+ }
68
+
69
+ function updateWords(turnId: string, words: Word[]): void {
70
+ const result = updateTurnWords(turns.value, turnId, words)
71
+ if (!result) return
72
+ turns.value = result.turns
73
+ emit("turn:update", { turn: result.updated, translationId: id })
74
+ }
75
+
76
+ function prependTurns(newTurns: Turn[]): void {
77
+ ensureSpeakersFromTurns(newTurns, speakersEnsure)
78
+ turns.value = prependTurnsHelper(turns.value, newTurns)
79
+ rebuildIndex()
80
+ }
81
+
82
+ function setTurns(newTurns: Turn[]): void {
83
+ ensureSpeakersFromTurns(newTurns, speakersEnsure)
84
+ turns.value = newTurns
85
+ rebuildIndex()
86
+ emit("translation:sync", { translationId: id })
87
+ }
88
+
89
+ function replaceTurns(newTurns: Turn[]): void {
90
+ turns.value = newTurns
91
+ rebuildIndex()
92
+ }
93
+
94
+ function updateOrCreateTurnSilent(turn: Turn): void {
95
+ const idx = indexMap.get(turn.id)
96
+ if (idx !== undefined) {
97
+ turns.value[idx] = turn
98
+ } else {
99
+ indexMap.set(turn.id, turns.value.length)
100
+ turns.value.push(turn)
101
+ }
102
+ }
103
+
104
+ function hasTurn(turnId: string): boolean {
105
+ return indexMap.has(turnId)
106
+ }
107
+
108
+ function getTurn(turnId: string): Turn | undefined {
109
+ const index = indexMap.get(turnId)
110
+ if (index === undefined) return undefined
111
+ return turns.value[index]
112
+ }
113
+
114
+ return {
115
+ id,
116
+ languages,
117
+ isSource,
118
+ audio,
119
+ turns,
120
+ lastModifiedAt,
121
+ setLastModifiedAt,
122
+ addTurn,
123
+ prependTurns,
124
+ updateTurn,
125
+ removeTurn,
126
+ updateWords,
127
+ setTurns,
128
+ replaceTurns,
129
+ updateOrCreateTurnSilent,
130
+ hasTurn,
131
+ getTurn,
132
+ }
133
+ }