@tencent-rtc/trtc-agent-skills 0.1.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 (205) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/README.zh.md +173 -0
  4. package/bin/cli.js +434 -0
  5. package/knowledge-base/index.yaml +454 -0
  6. package/knowledge-base/platform-slice-template.md +233 -0
  7. package/knowledge-base/scenario-spec.md +350 -0
  8. package/knowledge-base/scenarios/conference/base/general-conference.md +365 -0
  9. package/knowledge-base/scenarios/conference/base/webinar-conference.md +130 -0
  10. package/knowledge-base/scenarios/conference/medical/1v1-video-consultation.md +145 -0
  11. package/knowledge-base/scenarios/conference/medical/medical-multidoctor-consultation.md +113 -0
  12. package/knowledge-base/scenarios/live/entertainment-live-room.md +118 -0
  13. package/knowledge-base/slice-spec.md +546 -0
  14. package/knowledge-base/slices/conference/web/ai-tools.md +225 -0
  15. package/knowledge-base/slices/conference/web/beauty-effects.md +188 -0
  16. package/knowledge-base/slices/conference/web/device-control.md +338 -0
  17. package/knowledge-base/slices/conference/web/login-auth.md +261 -0
  18. package/knowledge-base/slices/conference/web/network-quality.md +190 -0
  19. package/knowledge-base/slices/conference/web/official-roomkit-api.md +298 -0
  20. package/knowledge-base/slices/conference/web/official-roomkit-login-ui.md +246 -0
  21. package/knowledge-base/slices/conference/web/participant-list.md +238 -0
  22. package/knowledge-base/slices/conference/web/participant-management.md +718 -0
  23. package/knowledge-base/slices/conference/web/prejoin-check.md +293 -0
  24. package/knowledge-base/slices/conference/web/room-call.md +213 -0
  25. package/knowledge-base/slices/conference/web/room-chat.md +426 -0
  26. package/knowledge-base/slices/conference/web/room-lifecycle.md +534 -0
  27. package/knowledge-base/slices/conference/web/room-schedule.md +281 -0
  28. package/knowledge-base/slices/conference/web/screen-share.md +211 -0
  29. package/knowledge-base/slices/conference/web/video-layout.md +675 -0
  30. package/knowledge-base/slices/conference/web/virtual-background.md +197 -0
  31. package/knowledge-base/slices/conference/web/webinar-interaction.md +206 -0
  32. package/knowledge-base/slices/live/anchor-lifecycle.md +122 -0
  33. package/knowledge-base/slices/live/anchor-preview.md +90 -0
  34. package/knowledge-base/slices/live/anchor-room-config.md +104 -0
  35. package/knowledge-base/slices/live/audience-list.md +86 -0
  36. package/knowledge-base/slices/live/audience-manage.md +92 -0
  37. package/knowledge-base/slices/live/audience-watch.md +85 -0
  38. package/knowledge-base/slices/live/audio.md +116 -0
  39. package/knowledge-base/slices/live/barrage.md +88 -0
  40. package/knowledge-base/slices/live/beauty.md +99 -0
  41. package/knowledge-base/slices/live/coguest-apply.md +105 -0
  42. package/knowledge-base/slices/live/device-control.md +91 -0
  43. package/knowledge-base/slices/live/error-codes.md +167 -0
  44. package/knowledge-base/slices/live/gift.md +84 -0
  45. package/knowledge-base/slices/live/ios/.gitkeep +0 -0
  46. package/knowledge-base/slices/live/ios/anchor-lifecycle.md +313 -0
  47. package/knowledge-base/slices/live/ios/anchor-preview.md +228 -0
  48. package/knowledge-base/slices/live/ios/anchor-room-config.md +257 -0
  49. package/knowledge-base/slices/live/ios/audience-list.md +353 -0
  50. package/knowledge-base/slices/live/ios/audience-manage.md +381 -0
  51. package/knowledge-base/slices/live/ios/audience-watch.md +286 -0
  52. package/knowledge-base/slices/live/ios/audio.md +373 -0
  53. package/knowledge-base/slices/live/ios/barrage.md +285 -0
  54. package/knowledge-base/slices/live/ios/beauty.md +323 -0
  55. package/knowledge-base/slices/live/ios/coguest-apply.md +506 -0
  56. package/knowledge-base/slices/live/ios/device-control.md +286 -0
  57. package/knowledge-base/slices/live/ios/error-codes.md +270 -0
  58. package/knowledge-base/slices/live/ios/gift.md +315 -0
  59. package/knowledge-base/slices/live/ios/live-list.md +269 -0
  60. package/knowledge-base/slices/live/ios/login-auth.md +247 -0
  61. package/knowledge-base/slices/live/live-list.md +82 -0
  62. package/knowledge-base/slices/live/login-auth.md +78 -0
  63. package/package.json +34 -0
  64. package/skills/trtc/SKILL.md +326 -0
  65. package/skills/trtc/room-builder/SKILL.md +138 -0
  66. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/README.md +108 -0
  67. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/docs/backend-contract.zh-CN.md +162 -0
  68. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/docs/integration.zh-CN.md +154 -0
  69. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/docs/theme.zh-CN.md +78 -0
  70. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/index.html +12 -0
  71. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/package.json +28 -0
  72. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/postcss.config.js +5 -0
  73. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/App.vue +25 -0
  74. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/ConsultationManagePanel.vue +838 -0
  75. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/LanguageSwitch.vue +102 -0
  76. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/LoadingSpinner.vue +6 -0
  77. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalAlert.vue +34 -0
  78. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalBusinessPanel.vue +148 -0
  79. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalButton.vue +49 -0
  80. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalConfirmDialog.vue +68 -0
  81. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalDataPanel.vue +196 -0
  82. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalRecordPanel.vue +270 -0
  83. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/PrescriptionPanel.vue +363 -0
  84. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/basic-info-config.ts +29 -0
  85. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/lib-generate-test-usersig-es.min.d.ts +4 -0
  86. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/lib-generate-test-usersig-es.min.js +2 -0
  87. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/runtime-config.ts +12 -0
  88. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/env.d.ts +32 -0
  89. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationChatPanel.vue +123 -0
  90. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationMembersPanel.vue +230 -0
  91. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationTranscriptionPanel.vue +135 -0
  92. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationVideoStage.vue +113 -0
  93. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/InviteDoctorDialog.vue +132 -0
  94. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/KickMemberConfirmDialog.vue +50 -0
  95. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/types.ts +77 -0
  96. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationChat.ts +97 -0
  97. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationDevices.ts +48 -0
  98. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationParticipants.ts +121 -0
  99. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationPermissions.ts +25 -0
  100. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/utils.ts +70 -0
  101. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/en-US/index.ts +553 -0
  102. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/index.ts +25 -0
  103. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/medicalTranslate.ts +85 -0
  104. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/state.ts +49 -0
  105. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/zh-CN/index.ts +463 -0
  106. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/main.ts +12 -0
  107. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/mock/appointments.ts +96 -0
  108. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/mock/users.ts +79 -0
  109. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/router/index.ts +63 -0
  110. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/index.ts +25 -0
  111. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/appointmentService.ts +77 -0
  112. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/authService.ts +38 -0
  113. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/launchContext.ts +31 -0
  114. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/userService.ts +35 -0
  115. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/mock/appointmentService.ts +43 -0
  116. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/mock/authService.ts +33 -0
  117. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/mock/userService.ts +43 -0
  118. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/types.ts +135 -0
  119. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/shared/icons.ts +53 -0
  120. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/styles/index.css +106 -0
  121. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/styles/tailwind.css +3 -0
  122. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/styles/theme.css +209 -0
  123. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/auth.ts +50 -0
  124. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/format.ts +24 -0
  125. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/navigation.ts +12 -0
  126. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/session.ts +28 -0
  127. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/DoctorConsultationView.vue +777 -0
  128. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/DoctorDashboardView.vue +678 -0
  129. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/LoginView.vue +441 -0
  130. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientConsultationFinishedView.vue +185 -0
  131. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientConsultationView.vue +1003 -0
  132. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientSelectDoctorView.vue +317 -0
  133. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientWaitingView.vue +454 -0
  134. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/tsconfig.json +21 -0
  135. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/tsconfig.node.json +8 -0
  136. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/vite.config.ts +17 -0
  137. package/skills/trtc/room-builder/templates/scenarios/medical-consultation//346/216/245/345/205/245/350/257/264/346/230/216.md +6 -0
  138. package/skills/trtc/room-builder/tools/render_ai_instructions.py +226 -0
  139. package/skills/trtc-apply/SKILL.md +97 -0
  140. package/skills/trtc-apply/guardrails/apply_lib/__init__.py +0 -0
  141. package/skills/trtc-apply/guardrails/apply_lib/__pycache__/__init__.cpython-313.pyc +0 -0
  142. package/skills/trtc-apply/guardrails/apply_lib/__pycache__/rule_parser.cpython-313.pyc +0 -0
  143. package/skills/trtc-apply/guardrails/apply_lib/rule_parser.py +268 -0
  144. package/skills/trtc-docs/SKILL.md +207 -0
  145. package/skills/trtc-onboarding/SKILL.md +839 -0
  146. package/skills/trtc-onboarding/reference/path-a1-demo.md +103 -0
  147. package/skills/trtc-onboarding/reference/path-a2-integrate.md +693 -0
  148. package/skills/trtc-onboarding/reference/path-b-troubleshoot.md +115 -0
  149. package/skills/trtc-onboarding/reference/path-c-expand.md +43 -0
  150. package/skills/trtc-onboarding/reference/reporting-protocol.md +174 -0
  151. package/skills/trtc-onboarding/reference/supported-matrix.md +100 -0
  152. package/skills/trtc-onboarding/reference/usersig-handling.md +140 -0
  153. package/skills/trtc-search/SKILL.md +221 -0
  154. package/skills/trtc-topic/SKILL.md +638 -0
  155. package/skills/trtc-topic/guardrails/__pycache__/gate_slice_read.cpython-313.pyc +0 -0
  156. package/skills/trtc-topic/guardrails/__pycache__/gate_slice_write.cpython-313.pyc +0 -0
  157. package/skills/trtc-topic/guardrails/__pycache__/stop_require_apply_evidence.cpython-313.pyc +0 -0
  158. package/skills/trtc-topic/guardrails/gate_slice_read.py +133 -0
  159. package/skills/trtc-topic/guardrails/gate_slice_write.py +169 -0
  160. package/skills/trtc-topic/guardrails/stop_require_apply_evidence.py +97 -0
  161. package/skills/trtc-topic/references/execution-units.yaml +58 -0
  162. package/skills/trtc-topic/runtime/README.md +50 -0
  163. package/skills/trtc-topic/runtime/RUNTIME.md +128 -0
  164. package/skills/trtc-topic/runtime/lib/__init__.py +0 -0
  165. package/skills/trtc-topic/runtime/lib/platforms.py +194 -0
  166. package/skills/trtc-topic/runtime/package-lock.json +1211 -0
  167. package/skills/trtc-topic/runtime/package.json +13 -0
  168. package/skills/trtc-topic/runtime/telemetry-bridge.mjs +339 -0
  169. package/skills/trtc-topic/runtime/telemetry_collector.py +293 -0
  170. package/skills/trtc-topic/scripts/STATE-MACHINE-GUIDE.md +186 -0
  171. package/skills/trtc-topic/scripts/__pycache__/apply.cpython-313.pyc +0 -0
  172. package/skills/trtc-topic/scripts/apply.py +581 -0
  173. package/skills/trtc-topic/scripts/finalize_session.py +113 -0
  174. package/skills/trtc-topic/scripts/init_slice_queue.py +96 -0
  175. package/skills/trtc-topic/scripts/lib/__pycache__/state_machine.cpython-313.pyc +0 -0
  176. package/skills/trtc-topic/scripts/lib/state_machine.py +328 -0
  177. package/skills/trtc-topic/scripts/next_slice.py +137 -0
  178. package/skills/trtc-topic/tests/README.md +70 -0
  179. package/skills/trtc-topic/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc +0 -0
  180. package/skills/trtc-topic/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  181. package/skills/trtc-topic/tests/__pycache__/test_apply_cli.cpython-313-pytest-9.0.2.pyc +0 -0
  182. package/skills/trtc-topic/tests/__pycache__/test_apply_cli.cpython-313-pytest-9.0.3.pyc +0 -0
  183. package/skills/trtc-topic/tests/__pycache__/test_end_to_end.cpython-313-pytest-9.0.2.pyc +0 -0
  184. package/skills/trtc-topic/tests/__pycache__/test_end_to_end.cpython-313-pytest-9.0.3.pyc +0 -0
  185. package/skills/trtc-topic/tests/__pycache__/test_finalize_session.cpython-313-pytest-9.0.2.pyc +0 -0
  186. package/skills/trtc-topic/tests/__pycache__/test_finalize_session.cpython-313-pytest-9.0.3.pyc +0 -0
  187. package/skills/trtc-topic/tests/__pycache__/test_gates.cpython-313-pytest-9.0.2.pyc +0 -0
  188. package/skills/trtc-topic/tests/__pycache__/test_gates.cpython-313-pytest-9.0.3.pyc +0 -0
  189. package/skills/trtc-topic/tests/__pycache__/test_session_resolver.cpython-313-pytest-9.0.2.pyc +0 -0
  190. package/skills/trtc-topic/tests/__pycache__/test_session_resolver.cpython-313-pytest-9.0.3.pyc +0 -0
  191. package/skills/trtc-topic/tests/__pycache__/test_state_machine.cpython-313-pytest-9.0.2.pyc +0 -0
  192. package/skills/trtc-topic/tests/__pycache__/test_state_machine.cpython-313-pytest-9.0.3.pyc +0 -0
  193. package/skills/trtc-topic/tests/__pycache__/test_stop_require_apply.cpython-313-pytest-9.0.2.pyc +0 -0
  194. package/skills/trtc-topic/tests/__pycache__/test_stop_require_apply.cpython-313-pytest-9.0.3.pyc +0 -0
  195. package/skills/trtc-topic/tests/__pycache__/test_topic_skill_invariants.cpython-313-pytest-9.0.2.pyc +0 -0
  196. package/skills/trtc-topic/tests/__pycache__/test_topic_skill_invariants.cpython-313-pytest-9.0.3.pyc +0 -0
  197. package/skills/trtc-topic/tests/conftest.py +72 -0
  198. package/skills/trtc-topic/tests/test_apply_cli.py +480 -0
  199. package/skills/trtc-topic/tests/test_end_to_end.py +305 -0
  200. package/skills/trtc-topic/tests/test_finalize_session.py +51 -0
  201. package/skills/trtc-topic/tests/test_gates.py +316 -0
  202. package/skills/trtc-topic/tests/test_session_resolver.py +260 -0
  203. package/skills/trtc-topic/tests/test_state_machine.py +414 -0
  204. package/skills/trtc-topic/tests/test_stop_require_apply.py +99 -0
  205. package/skills/trtc-topic/tests/test_topic_skill_invariants.py +130 -0
@@ -0,0 +1,77 @@
1
+ import type { RoomParticipant } from 'tuikit-atomicx-vue3';
2
+
3
+ export type ConsultationRole = 'primaryDoctor' | 'consultingDoctor' | 'patient';
4
+
5
+ export interface ConsultationPermissions {
6
+ canInviteDoctor: boolean;
7
+ canCancelInvite: boolean;
8
+ canKickMember: boolean;
9
+ canManagePatientDevice: boolean;
10
+ }
11
+
12
+ export interface ConsultationVideoMember {
13
+ userId: string;
14
+ displayName: string;
15
+ avatarText: string;
16
+ role: ConsultationRole;
17
+ roleLabel: string;
18
+ participant: RoomParticipant;
19
+ videoReady: boolean;
20
+ isSelf: boolean;
21
+ }
22
+
23
+ export interface ConsultationMemberCard {
24
+ userId: string;
25
+ userName: string;
26
+ isPatient: boolean;
27
+ roleLabel: string;
28
+ roleClass: string;
29
+ avatarClass: string;
30
+ cameraOn: boolean;
31
+ microphoneOn: boolean;
32
+ pending: boolean;
33
+ cancelable: boolean;
34
+ }
35
+
36
+ export interface DoctorInviteListItem {
37
+ userId: string;
38
+ userName: string;
39
+ department?: string;
40
+ title?: string;
41
+ inviteStatus: 'idle' | 'pending' | 'joined';
42
+ }
43
+
44
+ export interface ConsultationTranscriptItem {
45
+ segmentId: string;
46
+ speakerUserId: string;
47
+ sourceText: string;
48
+ timestamp: number;
49
+ }
50
+
51
+ export interface ConsultationCallEvent {
52
+ call?: {
53
+ roomId?: string;
54
+ callee?: {
55
+ userId?: string;
56
+ };
57
+ inviter?: {
58
+ userId?: string;
59
+ };
60
+ caller?: {
61
+ userId?: string;
62
+ };
63
+ };
64
+ roomInfo?: {
65
+ roomId?: string;
66
+ };
67
+ inviter?: {
68
+ userId?: string;
69
+ };
70
+ senderUserId?: string;
71
+ }
72
+
73
+ export interface ConsultationParticipantEvent {
74
+ userInfo?: {
75
+ userId?: string;
76
+ };
77
+ }
@@ -0,0 +1,97 @@
1
+ import { computed, ref, type Ref } from 'vue';
2
+ import {
3
+ useConversationListState,
4
+ useMessageInputState,
5
+ useMessageListState,
6
+ } from 'tuikit-atomicx-vue3';
7
+ import {
8
+ normalizeConsultationTimestamp,
9
+ type ConsultationMessageLike,
10
+ } from '@/features/consultation/utils';
11
+ import { medicalT } from '@/i18n/medicalTranslate';
12
+
13
+ export function useConsultationChat(options: {
14
+ peerUserId: Ref<string | undefined>;
15
+ sessionStartedAt: Ref<number>;
16
+ emptyErrorText?: string;
17
+ }) {
18
+ const { createC2CConversation, setActiveConversation, activeConversation } =
19
+ useConversationListState();
20
+ const { messageList } = useMessageListState();
21
+ const { sendMessage } = useMessageInputState();
22
+
23
+ const chatInput = ref('');
24
+ const chatError = ref('');
25
+ const isSendingMessage = ref(false);
26
+
27
+ const chatMessages = computed(() =>
28
+ ([...(messageList.value ?? [])] as unknown as ConsultationMessageLike[])
29
+ .filter(
30
+ item =>
31
+ normalizeConsultationTimestamp((item as { time?: number }).time) >=
32
+ options.sessionStartedAt.value
33
+ )
34
+ .slice(-120)
35
+ );
36
+
37
+ async function ensureChatConversation() {
38
+ chatError.value = '';
39
+ if (!options.peerUserId.value) {
40
+ return;
41
+ }
42
+ try {
43
+ const conversation = await createC2CConversation(options.peerUserId.value);
44
+ await setActiveConversation(conversation.conversationID);
45
+ } catch (error) {
46
+ chatError.value =
47
+ error instanceof Error
48
+ ? error.message
49
+ : options.emptyErrorText || medicalT('Medical.Chat.SessionInitFailed');
50
+ }
51
+ }
52
+
53
+ async function handleSendMessage() {
54
+ const content = chatInput.value.trim();
55
+ if (!content || isSendingMessage.value) {
56
+ return;
57
+ }
58
+ isSendingMessage.value = true;
59
+ chatError.value = '';
60
+ try {
61
+ if (!activeConversation.value?.conversationID) {
62
+ await ensureChatConversation();
63
+ }
64
+ await sendMessage(content);
65
+ chatInput.value = '';
66
+ } catch (error) {
67
+ chatError.value =
68
+ error instanceof Error
69
+ ? error.message
70
+ : medicalT('Medical.Chat.MessageSendFailed');
71
+ } finally {
72
+ isSendingMessage.value = false;
73
+ }
74
+ }
75
+
76
+ async function clearChatConversation() {
77
+ chatInput.value = '';
78
+ chatError.value = '';
79
+ isSendingMessage.value = false;
80
+ try {
81
+ await setActiveConversation('');
82
+ } catch {
83
+ // no-op: clearing local active conversation can fail silently
84
+ }
85
+ }
86
+
87
+ return {
88
+ activeConversation,
89
+ chatInput,
90
+ chatError,
91
+ isSendingMessage,
92
+ chatMessages,
93
+ ensureChatConversation,
94
+ handleSendMessage,
95
+ clearChatConversation,
96
+ };
97
+ }
@@ -0,0 +1,48 @@
1
+ import { ref } from 'vue';
2
+ import { useUIKit } from '@tencentcloud/uikit-base-component-vue3';
3
+
4
+ export type DeviceOpenResult =
5
+ | { status: 'fulfilled'; value: unknown }
6
+ | { status: 'rejected'; reason: unknown };
7
+
8
+ export function useConsultationDevices() {
9
+ const { t } = useUIKit();
10
+ const devicePermissionHint = ref('');
11
+
12
+ function getDeviceErrorHint(deviceLabel: string, error: unknown) {
13
+ const message = error instanceof Error ? error.message : '';
14
+ return t('Medical.Device.OpenFailed', {
15
+ device: deviceLabel,
16
+ message: message ? t('Medical.Device.ErrorInfo', { message }) : '',
17
+ });
18
+ }
19
+
20
+ function updateDevicePermissionHint(results: DeviceOpenResult[]) {
21
+ const failedLabels = results
22
+ .map((result, index) => ({
23
+ result,
24
+ label: index === 0 ? t('Medical.Device.Microphone') : t('Medical.Device.Camera'),
25
+ }))
26
+ .filter(
27
+ item =>
28
+ item.result.status === 'rejected' ||
29
+ (item.result.status === 'fulfilled' && item.result.value === false)
30
+ )
31
+ .map(item => item.label);
32
+
33
+ if (!failedLabels.length) {
34
+ devicePermissionHint.value = '';
35
+ return;
36
+ }
37
+
38
+ devicePermissionHint.value = t('Medical.Device.PermissionHint', {
39
+ devices: failedLabels.join('、'),
40
+ });
41
+ }
42
+
43
+ return {
44
+ devicePermissionHint,
45
+ getDeviceErrorHint,
46
+ updateDevicePermissionHint,
47
+ };
48
+ }
@@ -0,0 +1,121 @@
1
+ import { computed, ref, type Ref } from 'vue';
2
+ import { useUIKit } from '@tencentcloud/uikit-base-component-vue3';
3
+ import type { RoomParticipant } from 'tuikit-atomicx-vue3';
4
+ import type { MedicalUser } from '@/services/adapters';
5
+ import type {
6
+ ConsultationRole,
7
+ ConsultationVideoMember,
8
+ } from '@/features/consultation/types';
9
+
10
+ export function useConsultationParticipants(options: {
11
+ participantList: Ref<RoomParticipant[]>;
12
+ participantListWithVideo: Ref<RoomParticipant[]>;
13
+ localParticipant: Ref<RoomParticipant | null | undefined>;
14
+ primaryDoctorId: Ref<string | undefined>;
15
+ patientId: Ref<string | undefined>;
16
+ preferredFocusUserId: Ref<string | undefined>;
17
+ getDoctorById: (userId: string) => MedicalUser | null;
18
+ getPatientById: (userId: string) => MedicalUser | null;
19
+ selfDisplayName?: string;
20
+ }) {
21
+ const { t } = useUIKit();
22
+ const focusUserId = ref('');
23
+
24
+ function getRoleLabel(role: ConsultationRole) {
25
+ if (role === 'primaryDoctor') {
26
+ return t('Medical.Common.AttendingDoctor');
27
+ }
28
+ if (role === 'consultingDoctor') {
29
+ return t('Medical.Common.ConsultingDoctor');
30
+ }
31
+ return t('Medical.Common.Patient');
32
+ }
33
+
34
+ function getRoleFromUserId(
35
+ userId: string,
36
+ patientId: string | undefined,
37
+ primaryDoctorId: string | undefined
38
+ ): ConsultationRole {
39
+ if (userId === patientId) {
40
+ return 'patient';
41
+ }
42
+ if (userId === primaryDoctorId) {
43
+ return 'primaryDoctor';
44
+ }
45
+ return 'consultingDoctor';
46
+ }
47
+
48
+ const videoReadyUserIdSet = computed(
49
+ () =>
50
+ new Set(
51
+ options.participantListWithVideo.value.map(item => String(item.userId))
52
+ )
53
+ );
54
+
55
+ const members = computed<ConsultationVideoMember[]>(() =>
56
+ options.participantList.value.map(item => {
57
+ const userId = String(item.userId);
58
+ const isSelf = userId === options.localParticipant.value?.userId;
59
+ const role: ConsultationRole = getRoleFromUserId(
60
+ userId,
61
+ options.patientId.value,
62
+ options.primaryDoctorId.value
63
+ );
64
+ const doctorInfo = options.getDoctorById(userId);
65
+ const patientInfo = options.getPatientById(userId);
66
+ const rawDisplayName =
67
+ item.nameCard ||
68
+ doctorInfo?.userName ||
69
+ patientInfo?.userName ||
70
+ item.userName ||
71
+ userId;
72
+ const displayName = isSelf
73
+ ? options.selfDisplayName ||
74
+ `${rawDisplayName} (${t('Medical.Common.Me')})`
75
+ : rawDisplayName;
76
+
77
+ return {
78
+ userId,
79
+ displayName,
80
+ avatarText: isSelf
81
+ ? options.selfDisplayName || t('Medical.Common.Me')
82
+ : String(rawDisplayName).charAt(0),
83
+ role,
84
+ roleLabel: getRoleLabel(role),
85
+ participant: item,
86
+ videoReady: videoReadyUserIdSet.value.has(userId),
87
+ isSelf,
88
+ };
89
+ })
90
+ );
91
+
92
+ const mainMember = computed(() => {
93
+ const list = members.value;
94
+ return (
95
+ list.find(item => item.userId === focusUserId.value) ||
96
+ list.find(item => item.userId === options.preferredFocusUserId.value) ||
97
+ list.find(
98
+ item => item.userId === options.localParticipant.value?.userId
99
+ ) ||
100
+ list[0] ||
101
+ null
102
+ );
103
+ });
104
+
105
+ const thumbnailMembers = computed(() =>
106
+ members.value.filter(item => item.userId !== mainMember.value?.userId)
107
+ );
108
+
109
+ function focusParticipant(userId: string) {
110
+ focusUserId.value = userId;
111
+ }
112
+
113
+ return {
114
+ focusUserId,
115
+ videoReadyUserIdSet,
116
+ members,
117
+ mainMember,
118
+ thumbnailMembers,
119
+ focusParticipant,
120
+ };
121
+ }
@@ -0,0 +1,25 @@
1
+ import { computed, type Ref } from 'vue';
2
+ import type { ConsultationPermissions } from '@/features/consultation/types';
3
+
4
+ export function useConsultationPermissions(options: {
5
+ currentUserId: Ref<string | undefined>;
6
+ primaryDoctorId: Ref<string | undefined>;
7
+ }) {
8
+ const isPrimaryDoctor = computed(
9
+ () =>
10
+ !!options.currentUserId.value &&
11
+ options.currentUserId.value === options.primaryDoctorId.value
12
+ );
13
+
14
+ const permissions = computed<ConsultationPermissions>(() => ({
15
+ canInviteDoctor: isPrimaryDoctor.value,
16
+ canCancelInvite: isPrimaryDoctor.value,
17
+ canKickMember: isPrimaryDoctor.value,
18
+ canManagePatientDevice: isPrimaryDoctor.value,
19
+ }));
20
+
21
+ return {
22
+ isPrimaryDoctor,
23
+ permissions,
24
+ };
25
+ }
@@ -0,0 +1,70 @@
1
+ import { MessageType } from 'tuikit-atomicx-vue3';
2
+ import { medicalT } from '@/i18n/medicalTranslate';
3
+
4
+ export interface ConsultationMessageLike {
5
+ ID?: string;
6
+ flow?: string;
7
+ time?: number;
8
+ type?: MessageType;
9
+ payload?: {
10
+ text?: string;
11
+ };
12
+ getMessageContent?: () => {
13
+ text?: string;
14
+ };
15
+ }
16
+
17
+ export function normalizeConsultationTimestamp(timestamp?: number) {
18
+ if (!timestamp) {
19
+ return 0;
20
+ }
21
+ return timestamp < 1_000_000_000_000 ? timestamp * 1000 : timestamp;
22
+ }
23
+
24
+ export function formatConsultationClock(timestamp?: number, withSeconds = true) {
25
+ const timeValue = normalizeConsultationTimestamp(timestamp);
26
+ if (!timeValue) {
27
+ return '';
28
+ }
29
+ const date = new Date(timeValue);
30
+ const hours = `${date.getHours()}`.padStart(2, '0');
31
+ const minutes = `${date.getMinutes()}`.padStart(2, '0');
32
+ if (!withSeconds) {
33
+ return `${hours}:${minutes}`;
34
+ }
35
+ const seconds = `${date.getSeconds()}`.padStart(2, '0');
36
+ return `${hours}:${minutes}:${seconds}`;
37
+ }
38
+
39
+ export function getConsultationMessageText(message: ConsultationMessageLike) {
40
+ if (message.type === MessageType.TEXT || !message.type) {
41
+ if (typeof message.payload?.text === 'string') {
42
+ return message.payload.text;
43
+ }
44
+ const content =
45
+ typeof message.getMessageContent === 'function'
46
+ ? message.getMessageContent()
47
+ : undefined;
48
+ if (typeof content?.text === 'string') {
49
+ return content.text;
50
+ }
51
+ }
52
+ if (message.type === MessageType.IMAGE) {
53
+ return medicalT('Medical.Message.ImageMessage');
54
+ }
55
+ if (message.type === MessageType.VIDEO) {
56
+ return medicalT('Medical.Message.VideoMessage');
57
+ }
58
+ if (message.type === MessageType.FILE) {
59
+ return medicalT('Medical.Message.FileMessage');
60
+ }
61
+ return medicalT('Medical.Message.UnsupportedMessage');
62
+ }
63
+
64
+ export function formatElapsedDuration(seconds: number) {
65
+ const mins = Math.floor(seconds / 60);
66
+ const secs = seconds % 60;
67
+ return `${mins.toString().padStart(2, '0')}:${secs
68
+ .toString()
69
+ .padStart(2, '0')}`;
70
+ }