@3cr/viewer-browser 0.0.161 → 0.0.194

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 (229) hide show
  1. package/.circleci/config.yml +53 -0
  2. package/__tests__/index.spec.ts +31 -24
  3. package/components.d.ts +32 -0
  4. package/dist/Viewer3CR.js +44 -27
  5. package/dist/Viewer3CR.mjs +22734 -17747
  6. package/dist/Viewer3CR.umd.js +44 -27
  7. package/index.html +6 -8
  8. package/index.ts +9 -5
  9. package/package.json +5 -2
  10. package/playground/index.html +7 -10
  11. package/src/App.vue +12 -4
  12. package/src/__tests__/app.spec.ts +2 -13
  13. package/src/assets/magic_wand.svg +24 -0
  14. package/src/components/WebGL3DR.vue +53 -92
  15. package/src/components/__tests__/webgl3dr.spec.ts +29 -48
  16. package/src/{demo → components/demo}/DemoModal.vue +1 -1
  17. package/src/{demo → components/demo}/DemoPatientModal.vue +4 -1
  18. package/src/components/demo/__tests__/DemoModal.spec.ts +25 -0
  19. package/src/components/demo/__tests__/DemoPatientModal.spec.ts +37 -0
  20. package/src/components/demo/__tests__/options.spec.ts +25 -0
  21. package/src/components/demo/licence/DemoLicenceEnableCloudStorageModal.vue +64 -0
  22. package/src/{demo → components/demo}/licence/DemoLicenceInfoModal.vue +5 -4
  23. package/src/{demo → components/demo}/licence/DemoLicenceSendToPartyModal.vue +6 -4
  24. package/src/{demo → components/demo}/licence/DemoLicenceShareToMobileModal.vue +8 -6
  25. package/src/components/demo/licence/__tests__/DemoLicenceEnableCloudStorageModal.spec.ts +37 -0
  26. package/src/components/demo/licence/__tests__/DemoLicenceInfoModal.spec.ts +37 -0
  27. package/src/components/demo/licence/__tests__/DemoLicenceSendToPartyModal.spec.ts +36 -0
  28. package/src/components/demo/licence/__tests__/DemoLicenceShareToMobileModal.spec.ts +51 -0
  29. package/src/{demo → components/demo}/options.ts +18 -20
  30. package/src/components/demo/patient/DemoPatientEnableCloudStorageModal.vue +64 -0
  31. package/src/{demo → components/demo}/patient/DemoPatientInfoModal.vue +5 -4
  32. package/src/{demo → components/demo}/patient/DemoPatientSendToPartyModal.vue +4 -3
  33. package/src/{demo → components/demo}/patient/DemoPatientShareToMobileModal.vue +8 -6
  34. package/src/components/demo/patient/__tests__/DemoPatientEnableCloudStorageModal.spec.ts +37 -0
  35. package/src/components/demo/patient/__tests__/DemoPatientInfoModal.spec.ts +37 -0
  36. package/src/components/demo/patient/__tests__/DemoPatientSendToPartyModal.spec.ts +36 -0
  37. package/src/components/demo/patient/__tests__/DemoPatientShareToMobileModal.spec.ts +51 -0
  38. package/src/components/loading/LoadingSpinner.vue +1 -1
  39. package/src/components/modal/ActionRail.vue +96 -0
  40. package/src/components/modal/AskAI.vue +250 -0
  41. package/src/components/modal/CloseViewerModal.vue +104 -0
  42. package/src/components/modal/MftpWebGL3DRModal.vue +415 -834
  43. package/src/components/modal/ViewerActionRail.vue +123 -0
  44. package/src/components/modal/ViewerAnnotationModal.vue +115 -0
  45. package/src/components/modal/ViewerAnnotations.vue +283 -0
  46. package/src/components/modal/ViewerDisplaySettings.vue +102 -0
  47. package/src/components/modal/ViewerNavigationDrawer.vue +90 -0
  48. package/src/components/modal/ViewerNavigationDrawerContent.vue +126 -0
  49. package/src/components/modal/ViewerNavigationDrawerFooter.vue +111 -0
  50. package/src/components/modal/ViewerNavigationDrawerHeader.vue +63 -0
  51. package/src/components/modal/__tests__/CloseViewerModal.spec.ts +60 -0
  52. package/src/components/modal/__tests__/{mftp-webgl-3dr-modal.spec.ts → MftpWebGL3DRModal.spec.ts} +47 -298
  53. package/src/components/modal/__tests__/ViewerAnnotationModal.spec.ts +79 -0
  54. package/src/components/modal/__tests__/ViewerDisplaySettings.spec.ts +61 -0
  55. package/src/components/modal/__tests__/ViewerNavigationDrawer.spec.ts +32 -0
  56. package/src/components/modal/__tests__/ViewerNavigationDrawerContent.spec.ts +29 -0
  57. package/src/components/modal/__tests__/ViewerNavigationDrawerFooter.spec.ts +43 -0
  58. package/src/components/modal/__tests__/ViewerNavigationDrawerHeader.spec.ts +37 -0
  59. package/src/components/modal/actions/Action.vue +40 -0
  60. package/src/components/modal/actions/Flip3dAction.vue +79 -0
  61. package/src/components/modal/actions/FlipHorizontalAction.vue +36 -0
  62. package/src/components/modal/actions/FlipVerticalAction.vue +36 -0
  63. package/src/components/modal/actions/FullscreenAction.vue +47 -0
  64. package/src/components/modal/actions/NavigationCubeAction.vue +33 -0
  65. package/src/components/modal/actions/PanAction.vue +78 -0
  66. package/src/components/modal/actions/ResetViewAction.vue +29 -0
  67. package/src/components/modal/actions/Rotate2dAction.vue +78 -0
  68. package/src/components/modal/actions/Slice3dAction.vue +71 -0
  69. package/src/components/modal/actions/ZoomAction.vue +70 -0
  70. package/src/components/modal/actions/__tests__/Action.spec.ts +29 -0
  71. package/src/components/modal/actions/__tests__/Flip3dAction.spec.ts +48 -0
  72. package/src/components/modal/actions/__tests__/FlipHorizontalAction.spec.ts +17 -0
  73. package/src/components/modal/actions/__tests__/FlipVerticalAction.spec.ts +17 -0
  74. package/src/components/modal/actions/__tests__/FullscreenAction.spec.ts +28 -0
  75. package/src/components/modal/actions/__tests__/NavigationCubeAction.spec.ts +25 -0
  76. package/src/components/modal/actions/__tests__/PanAction.spec.ts +46 -0
  77. package/src/components/modal/actions/__tests__/ResetViewAction.spec.ts +17 -0
  78. package/src/components/modal/actions/__tests__/Rotate2dAction.spec.ts +23 -0
  79. package/src/components/modal/actions/__tests__/Slice3dAction.spec.ts +14 -0
  80. package/src/components/modal/actions/__tests__/ZoomAction.spec.ts +34 -0
  81. package/src/components/modal/composables/__tests__/useNavigationCubeObserver.spec.ts +56 -0
  82. package/src/components/modal/composables/useEventListener.ts +22 -0
  83. package/src/components/modal/composables/useNavigationCubeObserver.ts +104 -0
  84. package/src/components/selectors/ValueSelector.vue +30 -33
  85. package/src/components/selectors/__tests__/value-selector.spec.ts +1 -1
  86. package/src/components/sliders/DoubleSliderSelector.vue +79 -71
  87. package/src/components/sliders/VerticalSliderSelector.vue +12 -17
  88. package/src/components/sliders/__tests__/double-slider-selector.spec.ts +1 -1
  89. package/src/components/sliders/__tests__/vertical-slider-selector.spec.ts +1 -1
  90. package/src/dataLayer/__tests__/clamp.spec.ts +16 -0
  91. package/src/dataLayer/__tests__/eventHandlers.spec.ts +38 -0
  92. package/src/dataLayer/__tests__/getIconForPreset.spec.ts +40 -0
  93. package/src/dataLayer/__tests__/patchDataOverlay.spec.ts +88 -0
  94. package/src/dataLayer/__tests__/scanState.spec.ts +93 -0
  95. package/src/dataLayer/__tests__/useViewer3cr.spec.ts +10 -0
  96. package/src/dataLayer/__tests__/viewer3cr.spec.ts +331 -0
  97. package/src/dataLayer/clamp.ts +9 -0
  98. package/src/dataLayer/eventHandlers.ts +26 -0
  99. package/src/dataLayer/patchDataOverlay.ts +101 -0
  100. package/src/dataLayer/scanState.ts +105 -26
  101. package/src/dataLayer/useViewer3cr.ts +7 -0
  102. package/src/dataLayer/viewer3cr.ts +410 -0
  103. package/src/helpers/__tests__/layout-overlay-style.spec.ts +24 -22
  104. package/src/helpers/__tests__/model-helper.spec.ts +44 -13
  105. package/src/helpers/layoutOverlayStyle.ts +16 -27
  106. package/src/helpers/modelHelper.ts +62 -10
  107. package/src/models/Callbacks.ts +2 -2
  108. package/src/models/LoadViewerOptions.ts +2 -0
  109. package/src/models/LoadViewerPayload.ts +1 -0
  110. package/src/notifications/notification.ts +3 -4
  111. package/src/plugins/vuetify.ts +5 -0
  112. package/src/services/gpt/__tests__/gpt.service.spec.ts +27 -0
  113. package/src/services/gpt/gpt.service.ts +27 -0
  114. package/static/3cr-types-browser/index.ts +74 -0
  115. package/static/3cr-types-browser/types/Action.ts +6 -0
  116. package/static/3cr-types-browser/types/ActionData.ts +4 -0
  117. package/static/3cr-types-browser/types/AlphaKeys.ts +5 -0
  118. package/static/3cr-types-browser/types/AnchorPoint.ts +12 -0
  119. package/static/3cr-types-browser/types/CallToAction.ts +5 -0
  120. package/static/3cr-types-browser/types/ColourData.ts +7 -0
  121. package/static/3cr-types-browser/types/ColourPresetData.ts +9 -0
  122. package/static/3cr-types-browser/types/CurrentDataOverlayState.ts +6 -0
  123. package/static/3cr-types-browser/types/CurrentScanState.ts +22 -0
  124. package/static/3cr-types-browser/types/DataOverlay.ts +22 -0
  125. package/static/3cr-types-browser/types/DataOverlayActions.ts +14 -0
  126. package/static/3cr-types-browser/types/DataOverlayData.ts +8 -0
  127. package/static/3cr-types-browser/types/DataOverlayEvent.ts +8 -0
  128. package/static/3cr-types-browser/types/DecryptionKey.ts +4 -0
  129. package/static/3cr-types-browser/types/DisplaySettings.ts +10 -0
  130. package/static/3cr-types-browser/types/EmptyPayload.ts +3 -0
  131. package/static/3cr-types-browser/types/EnumPayload.ts +4 -0
  132. package/static/3cr-types-browser/types/FileManagementActions.ts +11 -0
  133. package/static/3cr-types-browser/types/FlipValue.ts +7 -0
  134. package/static/3cr-types-browser/types/FrontEndInterfaces.ts +14 -0
  135. package/static/3cr-types-browser/types/GradientKeys.ts +7 -0
  136. package/static/3cr-types-browser/types/GreyscalePresetData.ts +6 -0
  137. package/static/3cr-types-browser/types/InitialDataOverlayState.ts +6 -0
  138. package/static/3cr-types-browser/types/InitialScanState.ts +19 -0
  139. package/static/3cr-types-browser/types/InteractionType.ts +8 -0
  140. package/static/3cr-types-browser/types/InteractivityActions.ts +6 -0
  141. package/static/3cr-types-browser/types/InteractivityState.ts +4 -0
  142. package/static/3cr-types-browser/types/InvertTransformData.ts +6 -0
  143. package/static/3cr-types-browser/types/LayoutActions.ts +6 -0
  144. package/static/3cr-types-browser/types/LayoutData.ts +7 -0
  145. package/static/3cr-types-browser/types/LoadDataSet.ts +6 -0
  146. package/static/3cr-types-browser/types/LoadSessionState.ts +4 -0
  147. package/static/3cr-types-browser/types/LocalLoadDataset.ts +3 -0
  148. package/static/3cr-types-browser/types/MovementData.ts +7 -0
  149. package/static/3cr-types-browser/types/NavigationCubeActions.ts +8 -0
  150. package/static/3cr-types-browser/types/NavigationCubeData.ts +12 -0
  151. package/static/3cr-types-browser/types/NavigationCubeTransform.ts +9 -0
  152. package/static/3cr-types-browser/types/NotificationPayload.ts +7 -0
  153. package/static/3cr-types-browser/types/NotificationsActions.ts +6 -0
  154. package/static/3cr-types-browser/types/Object.ts +1 -0
  155. package/static/3cr-types-browser/types/ObjectColour.ts +7 -0
  156. package/static/3cr-types-browser/types/ObjectIcon.ts +5 -0
  157. package/static/3cr-types-browser/types/ObjectInvert.ts +7 -0
  158. package/static/3cr-types-browser/types/ObjectSize.ts +7 -0
  159. package/static/3cr-types-browser/types/ObjectSize2D.ts +7 -0
  160. package/static/3cr-types-browser/types/ObjectVisible.ts +5 -0
  161. package/static/3cr-types-browser/types/PositionData.ts +14 -0
  162. package/static/3cr-types-browser/types/PresetsActions.ts +4 -0
  163. package/static/3cr-types-browser/types/RotationValue.ts +7 -0
  164. package/static/3cr-types-browser/types/ScanMovementActions.ts +27 -0
  165. package/static/3cr-types-browser/types/ScanMovementData.ts +3 -0
  166. package/static/3cr-types-browser/types/ScanOrientationActions.ts +6 -0
  167. package/static/3cr-types-browser/types/ScanStateActions.ts +4 -0
  168. package/static/3cr-types-browser/types/ScanView.ts +6 -0
  169. package/static/3cr-types-browser/types/SettingsData.ts +12 -0
  170. package/static/3cr-types-browser/types/SlicerData.ts +9 -0
  171. package/static/3cr-types-browser/types/SliderValue.ts +4 -0
  172. package/static/3cr-types-browser/types/SlidersActions.ts +18 -0
  173. package/static/3cr-types-browser/types/Vector2Data.ts +5 -0
  174. package/static/3cr-types-browser/types/Vector3Data.ts +6 -0
  175. package/static/3cr-types-browser/types/VectorMovementData.ts +8 -0
  176. package/static/3cr-types-browser/types/ViewInteractiveMode.ts +5 -0
  177. package/static/3cr-types-browser/types/ViewOrientation.ts +8 -0
  178. package/static/3cr-types-browser/types/ViewOrientations.ts +10 -0
  179. package/static/3cr-types-browser/types/ViewSelectionActions.ts +9 -0
  180. package/static/3cr-types-browser/types/ViewToggleData.ts +7 -0
  181. package/static/3cr-types-browser/types/VolumeOrientation.ts +7 -0
  182. package/test/helper.ts +10 -1
  183. package/test/setup.ts +13 -0
  184. package/tsconfig.json +1 -0
  185. package/vite.config.mts +1 -0
  186. package/coverage/3cr-viewer-browser/index.html +0 -116
  187. package/coverage/3cr-viewer-browser/index.ts.html +0 -199
  188. package/coverage/3cr-viewer-browser/src/App.vue.html +0 -316
  189. package/coverage/3cr-viewer-browser/src/components/WebGL3DR.vue.html +0 -442
  190. package/coverage/3cr-viewer-browser/src/components/icons/index.html +0 -116
  191. package/coverage/3cr-viewer-browser/src/components/icons/liver.vue.html +0 -148
  192. package/coverage/3cr-viewer-browser/src/components/index.html +0 -116
  193. package/coverage/3cr-viewer-browser/src/components/loading/LoadingSpinner.vue.html +0 -622
  194. package/coverage/3cr-viewer-browser/src/components/loading/index.html +0 -116
  195. package/coverage/3cr-viewer-browser/src/components/modal/MftpWebGL3DRModal.vue.html +0 -3118
  196. package/coverage/3cr-viewer-browser/src/components/modal/index.html +0 -116
  197. package/coverage/3cr-viewer-browser/src/components/selectors/ValueSelector.vue.html +0 -358
  198. package/coverage/3cr-viewer-browser/src/components/selectors/index.html +0 -116
  199. package/coverage/3cr-viewer-browser/src/components/sliders/DoubleSliderSelector.vue.html +0 -487
  200. package/coverage/3cr-viewer-browser/src/components/sliders/VerticalSliderSelector.vue.html +0 -358
  201. package/coverage/3cr-viewer-browser/src/components/sliders/index.html +0 -131
  202. package/coverage/3cr-viewer-browser/src/dataLayer/iconData.ts.html +0 -118
  203. package/coverage/3cr-viewer-browser/src/dataLayer/index.html +0 -146
  204. package/coverage/3cr-viewer-browser/src/dataLayer/payloadHandler.ts.html +0 -463
  205. package/coverage/3cr-viewer-browser/src/dataLayer/scanState.ts.html +0 -598
  206. package/coverage/3cr-viewer-browser/src/helpers/index.html +0 -146
  207. package/coverage/3cr-viewer-browser/src/helpers/layoutOverlayStyle.ts.html +0 -406
  208. package/coverage/3cr-viewer-browser/src/helpers/modelHelper.ts.html +0 -412
  209. package/coverage/3cr-viewer-browser/src/helpers/utils.ts.html +0 -133
  210. package/coverage/3cr-viewer-browser/src/index.html +0 -131
  211. package/coverage/3cr-viewer-browser/src/main.ts.html +0 -124
  212. package/coverage/3cr-viewer-browser/src/models/LoadViewerOptions.ts.html +0 -166
  213. package/coverage/3cr-viewer-browser/src/models/index.html +0 -116
  214. package/coverage/3cr-viewer-browser/src/notifications/index.html +0 -116
  215. package/coverage/3cr-viewer-browser/src/notifications/notification.ts.html +0 -238
  216. package/coverage/3cr-viewer-browser/src/plugins/index.html +0 -131
  217. package/coverage/3cr-viewer-browser/src/plugins/index.ts.html +0 -136
  218. package/coverage/3cr-viewer-browser/src/plugins/vuetify.ts.html +0 -220
  219. package/coverage/base.css +0 -224
  220. package/coverage/block-navigation.js +0 -87
  221. package/coverage/favicon.png +0 -0
  222. package/coverage/index.html +0 -296
  223. package/coverage/prettify.css +0 -1
  224. package/coverage/prettify.js +0 -2
  225. package/coverage/sort-arrow-sprite.png +0 -0
  226. package/coverage/sorter.js +0 -196
  227. package/src/dataLayer/__tests__/payload-handler.spec.ts +0 -214
  228. package/src/dataLayer/payloadHandler.ts +0 -138
  229. /package/src/dataLayer/{iconData.ts → getIconForPreset.ts} +0 -0
@@ -0,0 +1,37 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { mountVuetify, openAllModals } from "~/helper";
3
+ import { PRICING_URL } from "@/components/demo/options";
4
+ import DemoPatientInfoModal from "@/components/demo/patient/DemoPatientInfoModal.vue";
5
+
6
+ describe('DemoPatientInfoModal tests', () => {
7
+ it('should mount', () => {
8
+ const props = { modal: true, isModalOpen: true };
9
+ const wrapper = mountVuetify(DemoPatientInfoModal, props);
10
+ expect(wrapper).toBeTruthy();
11
+ });
12
+
13
+ it('should close modal', async () => {
14
+ const props = { modal: true, isModalOpen: true };
15
+ const wrapper = mountVuetify(DemoPatientInfoModal, props);
16
+ const close = wrapper.findComponent('[data-testid="close"]');
17
+ await close.trigger('click');
18
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
19
+ });
20
+
21
+ it('should open support url', async () => {
22
+ vi.stubGlobal('open', vi.fn());
23
+ const spy = vi.spyOn(window, 'open');
24
+ const props = { modal: true };
25
+ const wrapper = mountVuetify(DemoPatientInfoModal, props);
26
+ const button = wrapper.findComponent('[data-testid="confirm"]');
27
+ await button.trigger('click');
28
+ expect(spy).toHaveBeenCalledWith(PRICING_URL, '_self');
29
+ vi.restoreAllMocks();
30
+ });
31
+
32
+ it('should open via input', async () => {
33
+ const props = { modal: false, isModalOpen: true };
34
+ const wrapper = mountVuetify(DemoPatientInfoModal, props);
35
+ await openAllModals(wrapper);
36
+ });
37
+ });
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { mountVuetify, openAllModals } from "~/helper";
3
+ import { VTextField } from "vuetify/components";
4
+ import DemoPatientSendToPartyModal from "@/components/demo/patient/DemoPatientSendToPartyModal.vue";
5
+
6
+ describe('DemoPatientSendToPartyModal tests', () => {
7
+ it('should mount', () => {
8
+ const props = { modal: true };
9
+ const wrapper = mountVuetify(DemoPatientSendToPartyModal, props);
10
+ expect(wrapper).toBeTruthy();
11
+ });
12
+
13
+ it('should close modal', async () => {
14
+ const props = { modal: true };
15
+ const wrapper = mountVuetify(DemoPatientSendToPartyModal, props);
16
+ const close = wrapper.findComponent('[data-testid="close"]');
17
+ await close.trigger('click');
18
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
19
+ });
20
+
21
+ it('should enter email', async () => {
22
+ const props = { modal: true };
23
+ const wrapper = mountVuetify(DemoPatientSendToPartyModal, props);
24
+ const input = wrapper.getComponent(VTextField);
25
+ await input.setValue('example@singular.health');
26
+ await input.trigger('keyup.enter');
27
+ const confirm = wrapper.findComponent('[data-testid="confirm"]');
28
+ await confirm.trigger('click');
29
+ });
30
+
31
+ it('should open via input', async () => {
32
+ const props = { modal: false };
33
+ const wrapper = mountVuetify(DemoPatientSendToPartyModal, props);
34
+ await openAllModals(wrapper);
35
+ });
36
+ });
@@ -0,0 +1,51 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { mountVuetify, openAllModals } from "~/helper";
3
+ import DemoPatientShareToMobileModal from "@/components/demo/patient/DemoPatientShareToMobileModal.vue";
4
+ import { PRICING_URL } from "../../options";
5
+
6
+ describe('DemoPatientShareToMobileModal tests', () => {
7
+ it('should mount', () => {
8
+ const props = { modal: true };
9
+ const wrapper = mountVuetify(DemoPatientShareToMobileModal, props);
10
+ expect(wrapper).toBeTruthy();
11
+ });
12
+
13
+ it('should close modal', async () => {
14
+ const props = { modal: true };
15
+ const wrapper = mountVuetify(DemoPatientShareToMobileModal, props);
16
+ const close = wrapper.findComponent('[data-testid="close-1"]');
17
+ await close.trigger('click');
18
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
19
+ });
20
+
21
+ it('should go to next step and open url', async () => {
22
+ vi.stubGlobal('open', vi.fn());
23
+ const spy = vi.spyOn(window, 'open');
24
+ const props = { modal: true };
25
+ const wrapper = mountVuetify(DemoPatientShareToMobileModal, props);
26
+ const confirm1 = wrapper.findComponent('[data-testid="confirm-1"]');
27
+ await confirm1.trigger('click');
28
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
29
+ const confirm2 = wrapper.findComponent('[data-testid="confirm-2"]');
30
+ await confirm2.trigger('click');
31
+ expect(spy).toHaveBeenCalledWith(PRICING_URL, '_self');
32
+ vi.restoreAllMocks();
33
+ });
34
+
35
+ it('should go to next step and close', async () => {
36
+ const props = { modal: true };
37
+ const wrapper = mountVuetify(DemoPatientShareToMobileModal, props);
38
+ const confirm1 = wrapper.findComponent('[data-testid="confirm-1"]');
39
+ await confirm1.trigger('click');
40
+ expect(wrapper.emitted()['update:modal']).toBeTruthy();
41
+ const close2 = wrapper.findComponent('[data-testid="close-2"]');
42
+ await close2.trigger('click');
43
+ expect(wrapper.text()).toBe('');
44
+ });
45
+
46
+ it('should open via input', async () => {
47
+ const props = { modal: false };
48
+ const wrapper = mountVuetify(DemoPatientShareToMobileModal, props);
49
+ await openAllModals(wrapper);
50
+ });
51
+ });
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  export interface Props {
3
- text: string;
3
+ text?: string;
4
4
  }
5
5
 
6
6
  const props = withDefaults(defineProps<Props>(), {
@@ -0,0 +1,96 @@
1
+ <template>
2
+ <div ref="rail" class="action-rail d-flex">
3
+ <template v-for="(action, index) of visible">
4
+ <div class="visible-item">
5
+ <slot name="visible" :action="action" :index="index"></slot>
6
+ </div>
7
+ </template>
8
+ <v-menu v-if="showMenu" v-bind="menuProps" @update:model-value="emit('menuActive', $event)">
9
+ <template #activator="{ props }">
10
+ <v-btn v-bind="mergeProps(props, menuActivatorProps)" />
11
+ </template>
12
+ <v-list>
13
+ <template v-for="(action, index) of hidden" >
14
+ <div class="hidden-item">
15
+ <slot name="hidden" :action="action" :index="index"></slot>
16
+ </div>
17
+ </template>
18
+ </v-list>
19
+ </v-menu>
20
+ </div>
21
+ </template>
22
+
23
+ <script setup lang="ts">
24
+ import { computed, mergeProps, onBeforeUnmount, onMounted, ref } from 'vue';
25
+
26
+ type Action<T = Record<string, unknown>> = {
27
+ width: number
28
+ } & T;
29
+
30
+ interface Props {
31
+ actions?: Action[];
32
+ menuProps?: Record<string, unknown>;
33
+ menuActivatorProps?: Record<string, unknown>;
34
+ }
35
+
36
+ type Emits = {
37
+ menuActive: [boolean];
38
+ };
39
+
40
+ const props = withDefaults(defineProps<Props>(), {
41
+ actions: () => [],
42
+ menuProps: () => ({}),
43
+ menuActivatorProps: () => ({})
44
+ });
45
+
46
+ const emit = defineEmits<Emits>();
47
+
48
+ const rail = ref<HTMLDivElement>();
49
+ const splitIndex = ref<number>(0);
50
+
51
+ const observer = new ResizeObserver(entries => {
52
+ if (entries.length > 0) {
53
+ const railWidth = entries[0].contentRect.width;
54
+ calculateSplitIndex(railWidth);
55
+ }
56
+ });
57
+
58
+ const visible = computed(() => {
59
+ return props.actions.slice(0, splitIndex.value);
60
+ });
61
+
62
+ const hidden = computed(() => {
63
+ return props.actions.slice(splitIndex.value);
64
+ });
65
+
66
+ const showMenu = computed(() => {
67
+ return hidden.value.length > 0;
68
+ });
69
+
70
+ onMounted(() => {
71
+ observer.observe(rail.value!);
72
+ const rect = rail.value!.getBoundingClientRect();
73
+ calculateSplitIndex(rect.width);
74
+ });
75
+
76
+ onBeforeUnmount(() => {
77
+ observer.disconnect();
78
+ });
79
+
80
+ function calculateSplitIndex(railWidth: number): void {
81
+ const widths = props.actions.map(action => action.width);
82
+ let sum = 48;
83
+ for (const [idx, width] of widths.entries()) {
84
+ if (sum + width < railWidth) {
85
+ sum += width;
86
+ splitIndex.value = idx + 1;
87
+ }
88
+ }
89
+ }
90
+ </script>
91
+
92
+ <style lang="scss">
93
+ .action-rail {
94
+ min-width: 0;
95
+ }
96
+ </style>
@@ -0,0 +1,250 @@
1
+ <template>
2
+ <v-dialog v-model:model-value="modalState" max-width="800" theme="light">
3
+ <v-card class="prompt-box pa-2 py-4" rounded="xl">
4
+ <v-card-title class="text-body-1 d-flex align-center text-wrap mx-3 mb-4">
5
+ <span>{{ questionText }}</span>
6
+ </v-card-title>
7
+ <!-- <v-card-subtitle> From ChatGPT: </v-card-subtitle>-->
8
+ <div class="bordered-gradient-box" v-if="smartResponses">
9
+ <div class="prompt" style="font-size: 28px">
10
+ <!-- &#129668;-->
11
+ <v-img
12
+ src="@/assets/magic_wand.svg"
13
+ class="animated"
14
+ max-width="42"
15
+ />
16
+ &nbsp;&nbsp;&nbsp;<span
17
+ style="font-size: 18px"
18
+ v-html="smartResponses?.GptResponse"
19
+ >
20
+ </span>
21
+ </div>
22
+ </div>
23
+
24
+ <!-- <v-card-text v-html="smartResponses?.GptResponse"> </v-card-text>-->
25
+ <div class="prompt bordered-gradient-box" v-else>
26
+ <div class="loading"></div>
27
+ </div>
28
+ <v-card-text class="py-0">
29
+ <span
30
+ class="text-caption"
31
+ style="font-size: 10px !important; line-height: 10px !important"
32
+ >* 3Dicom's Annotiva AI is powered by AI Large Language Models (LLMs)
33
+ which generate general health information for improved patient
34
+ comprehension. <br />It is NOT for diagnostic usage.
35
+ </span>
36
+ </v-card-text>
37
+
38
+ <v-card-title
39
+ v-if="smartResponses"
40
+ class="text-body-1 d-flex align-center text-wrap mx-3 mt-6 mb-0"
41
+ >
42
+ Still Curious?
43
+ </v-card-title>
44
+ <v-card-text class="py-0">
45
+ <v-chip-group column v-model="item">
46
+ <v-chip
47
+ v-for="action in smartResponses?.FollowupQuestions"
48
+ style="
49
+ text-wrap: wrap;
50
+ word-wrap: break-word;
51
+ min-height: 36px;
52
+ background: linear-gradient(
53
+ to right,
54
+ rgba(38, 55, 245, 0.85),
55
+ rgba(145, 78, 245, 0.85)
56
+ );
57
+ "
58
+ class="text-wrap mb-1 px-4 v-chip--selected text-white"
59
+ @click="askFollowUpQuestion(action)"
60
+ >
61
+ {{ action.Question }}
62
+ </v-chip>
63
+ </v-chip-group>
64
+ </v-card-text>
65
+ </v-card>
66
+ </v-dialog>
67
+ </template>
68
+
69
+ <script setup lang="ts">
70
+ import { computed, ref, watch } from "vue";
71
+ import {
72
+ GptQuestion,
73
+ GptResponsePayload,
74
+ GptService,
75
+ } from "@/services/gpt/gpt.service";
76
+ const smartResponses = ref<GptResponsePayload | null>(null);
77
+ interface Props {
78
+ modal?: boolean;
79
+ questionText: string;
80
+ title: string;
81
+ questionKey: number;
82
+ }
83
+
84
+ type Emits = {
85
+ "update:modal": [value: boolean];
86
+ askFollowup: [value: GptQuestion];
87
+ };
88
+
89
+ const props = defineProps<Props>();
90
+
91
+ const emit = defineEmits<Emits>();
92
+
93
+ const item = ref();
94
+ const modalState = computed({
95
+ get(): boolean {
96
+ return props.modal;
97
+ },
98
+ set(value: boolean): void {
99
+ emit("update:modal", value);
100
+ },
101
+ });
102
+
103
+ watch(
104
+ modalState,
105
+ async (val: boolean) => {
106
+ if (val) {
107
+ smartResponses.value = null;
108
+ await getSmartResponses();
109
+ }
110
+ },
111
+ { immediate: true }
112
+ );
113
+
114
+ async function getSmartResponses(): Promise<void> {
115
+ smartResponses.value = await GptService.Instantiate()
116
+ .GenerateAnnotations(props.title, props.questionKey)
117
+ .then((data) => data.data);
118
+ item.value = smartResponses.value?.FollowupQuestions.findIndex(
119
+ (question) => question.ApiPreFilledRequestKey === props.questionKey
120
+ );
121
+ }
122
+
123
+ async function askFollowUpQuestion(question: GptQuestion): Promise<void> {
124
+ emit("askFollowup", question);
125
+ }
126
+ </script>
127
+ <style>
128
+ .loading {
129
+ font-size: 30px;
130
+ }
131
+ .loading:after {
132
+ overflow: hidden;
133
+ display: inline-block;
134
+ vertical-align: bottom;
135
+ -webkit-animation: ellipsis steps(4, end) 900ms infinite;
136
+ animation: ellipsis steps(4, end) 900ms infinite;
137
+ content: "\2026";
138
+ /* ascii code for the ellipsis character */
139
+ width: 0px;
140
+ }
141
+
142
+ @keyframes ellipsis {
143
+ to {
144
+ width: 40px;
145
+ }
146
+ }
147
+
148
+ @-webkit-keyframes ellipsis {
149
+ to {
150
+ width: 40px;
151
+ }
152
+ }
153
+
154
+ .prompt-box p {
155
+ font-weight: bold;
156
+ margin-bottom: 20px;
157
+ }
158
+
159
+ .prompt {
160
+ margin-left: 20px;
161
+ margin-right: 20px;
162
+ padding: 20px;
163
+ text-align: center;
164
+ color: black;
165
+ display: flex;
166
+ justify-content: space-between;
167
+ align-items: center;
168
+ margin-bottom: 15px;
169
+ z-index: 100;
170
+ position: relative;
171
+ }
172
+
173
+ .prompt::before {
174
+ content: "";
175
+ position: absolute;
176
+ inset: 0;
177
+ border-radius: 16px;
178
+ padding: 3px;
179
+ background: linear-gradient(to right, #6fb3f2, #4e51f5);
180
+
181
+ -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
182
+ -webkit-mask-composite: xor;
183
+ mask-composite: exclude;
184
+ }
185
+
186
+ .prompt span {
187
+ flex: 1;
188
+ text-align: left;
189
+ margin-right: 10px;
190
+ }
191
+
192
+ .icon {
193
+ background: #e0e0e0;
194
+ border-radius: 50%;
195
+ padding: 5px;
196
+ }
197
+
198
+ .record {
199
+ margin-top: 20px;
200
+ }
201
+
202
+ .record-button {
203
+ background: #4e51f5;
204
+ color: white;
205
+ padding: 10px 20px;
206
+ border: none;
207
+ border-radius: 20px;
208
+ font-size: 16px;
209
+ cursor: pointer;
210
+ }
211
+
212
+ .record-button:active {
213
+ background: #3b3edf;
214
+ }
215
+ .animated {
216
+ animation: pulse 2s infinite;
217
+ transition: all 2s ease-in-out;
218
+ }
219
+ @keyframes pulse {
220
+ 0% {
221
+ transform: scale(1);
222
+ }
223
+ 70% {
224
+ transform: scale(1.2);
225
+ }
226
+ }
227
+ .btn-grad {
228
+ background-image: linear-gradient(
229
+ to right,
230
+ #2b5876 0%,
231
+ #4e4376 51%,
232
+ #2b5876 100%
233
+ );
234
+ margin: 10px;
235
+ padding: 15px 45px;
236
+ text-align: center;
237
+ text-transform: uppercase;
238
+ transition: 0.5s;
239
+ background-size: 200% auto;
240
+ color: white;
241
+ box-shadow: 0 0 20px #eee;
242
+ border-radius: 10px;
243
+ display: block;
244
+ }
245
+ .btn-grad:hover {
246
+ background-position: right center; /* change the direction of the change here */
247
+ color: #fff;
248
+ text-decoration: none;
249
+ }
250
+ </style>
@@ -0,0 +1,104 @@
1
+ <template>
2
+ <v-dialog data-testid="dialog" v-model:model-value="modalState">
3
+ <v-card
4
+ class="pa-1 ma-auto position-relative motif-background"
5
+ width="600"
6
+ theme="dark"
7
+ >
8
+ <v-card-title class="text-center">Close Viewer?</v-card-title>
9
+ <v-card-text class="text-center mb-2">
10
+ Are you sure you want to close the Online Viewer?
11
+ </v-card-text>
12
+ <v-card-actions>
13
+ <v-btn
14
+ variant="flat"
15
+ color="secondary"
16
+ @click="modalState = false"
17
+ >
18
+ Cancel
19
+ </v-btn>
20
+ <v-spacer />
21
+ <v-btn
22
+ data-testid="close"
23
+ color="red"
24
+ variant="flat"
25
+ @click="closeModal"
26
+ v-if="showOption('OnSaveSession')"
27
+ >
28
+ Close without saving
29
+ </v-btn>
30
+ <v-btn
31
+ data-testid="save"
32
+ color="primary"
33
+ variant="flat"
34
+ @click="closeModalSave"
35
+ v-if="showOption('OnSaveSession')"
36
+ >
37
+ Save Session
38
+ </v-btn>
39
+ <v-btn
40
+ data-testid="close"
41
+ color="flat"
42
+ variant="tonal"
43
+ @click="closeModal" v-else>
44
+ Close Viewer
45
+ </v-btn>
46
+ </v-card-actions>
47
+ </v-card>
48
+ </v-dialog>
49
+ </template>
50
+
51
+ <script setup lang="ts">
52
+ import {computed} from "vue";
53
+ import {LoadViewerOptions} from "@/models/LoadViewerOptions";
54
+ import {dataOverlayState, scanState} from "@/dataLayer/scanState";
55
+
56
+ interface Props {
57
+ options: LoadViewerOptions,
58
+ modal?: boolean;
59
+ }
60
+
61
+ type Emits = {
62
+ 'update:modal': [boolean];
63
+ 'close': [void];
64
+ };
65
+
66
+ const props = withDefaults(defineProps<Props>(), {
67
+ modal: false
68
+ });
69
+
70
+ const emit = defineEmits<Emits>();
71
+
72
+ const modalState = computed({
73
+ get(): boolean {
74
+ return props.modal;
75
+ },
76
+ set(value: boolean): void {
77
+ emit('update:modal', value);
78
+ }
79
+ });
80
+
81
+ async function closeModal(): Promise<void> {
82
+ await executeOption('OnExitViewer');
83
+ modalState.value = false;
84
+ emit('close');
85
+ }
86
+
87
+ async function closeModalSave(): Promise<void> {
88
+ const scnState = scanState.value;
89
+ const dtaOverlayState = dataOverlayState.value.DataOverlay;
90
+ await executeOption('OnSaveSession', scnState, dtaOverlayState, null);
91
+ await closeModal();
92
+ }
93
+
94
+ function showOption(key: keyof LoadViewerOptions): boolean {
95
+ return props.options[key] !== undefined;
96
+ }
97
+
98
+ async function executeOption(key: keyof LoadViewerOptions, ...args: any[]): Promise<void> {
99
+ const functionToExecute = props.options[key];
100
+ if (functionToExecute !== undefined) {
101
+ await functionToExecute(...args);
102
+ }
103
+ }
104
+ </script>