@capytale/meta-player 0.0.1

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 (66) hide show
  1. package/.eslintrc.json +35 -0
  2. package/.prettierrc.json +4 -0
  3. package/README.md +27 -0
  4. package/index.html +15 -0
  5. package/package.json +48 -0
  6. package/public/themes/lara-dark-blue/fonts/Inter-italic.var.woff2 +0 -0
  7. package/public/themes/lara-dark-blue/fonts/Inter-roman.var.woff2 +0 -0
  8. package/public/themes/lara-dark-blue/theme.css +7015 -0
  9. package/public/themes/lara-light-blue/fonts/Inter-italic.var.woff2 +0 -0
  10. package/public/themes/lara-light-blue/fonts/Inter-roman.var.woff2 +0 -0
  11. package/public/themes/lara-light-blue/theme.css +7005 -0
  12. package/src/App.tsx +116 -0
  13. package/src/AppRedux.css +39 -0
  14. package/src/MetaPlayer.tsx +51 -0
  15. package/src/app/createAppSlice.ts +6 -0
  16. package/src/app/hooks.ts +12 -0
  17. package/src/app/store.ts +46 -0
  18. package/src/app.module.scss +56 -0
  19. package/src/demo.tsx +81 -0
  20. package/src/features/activityData/IsDirtySetter.tsx +17 -0
  21. package/src/features/activityData/OptionSetter.tsx +35 -0
  22. package/src/features/activityData/activityDataSlice.ts +250 -0
  23. package/src/features/activityData/metaPlayerOptions.ts +17 -0
  24. package/src/features/activityJS/ActivityJSProvider.tsx +110 -0
  25. package/src/features/activityJS/AfterSaveAction.tsx +23 -0
  26. package/src/features/activityJS/BeforeSaveAction.tsx +23 -0
  27. package/src/features/activityJS/Saver.tsx +147 -0
  28. package/src/features/activityJS/hooks.ts +93 -0
  29. package/src/features/activityJS/saverSlice.ts +58 -0
  30. package/src/features/layout/layoutSlice.ts +76 -0
  31. package/src/features/navbar/ActivityInfo.tsx +41 -0
  32. package/src/features/navbar/ActivityMenu.tsx +56 -0
  33. package/src/features/navbar/ActivityQuickActions.tsx +52 -0
  34. package/src/features/navbar/ActivitySidebarActions.tsx +52 -0
  35. package/src/features/navbar/CapytaleMenu.tsx +183 -0
  36. package/src/features/navbar/GradingNav.tsx +120 -0
  37. package/src/features/navbar/ReviewNavbar.tsx +18 -0
  38. package/src/features/navbar/SidebarContent.tsx +125 -0
  39. package/src/features/navbar/index.tsx +33 -0
  40. package/src/features/navbar/navbarSlice.ts +51 -0
  41. package/src/features/navbar/student-utils.ts +11 -0
  42. package/src/features/navbar/style.module.scss +162 -0
  43. package/src/features/pedago/AnswerSheetEditor.tsx +65 -0
  44. package/src/features/pedago/InstructionsEditor.tsx +82 -0
  45. package/src/features/pedago/index.tsx +219 -0
  46. package/src/features/pedago/style.module.scss +104 -0
  47. package/src/features/theming/ThemeSwitcher.tsx +51 -0
  48. package/src/features/theming/themingSlice.ts +93 -0
  49. package/src/hooks/index.ts +8 -0
  50. package/src/index.css +30 -0
  51. package/src/index.tsx +6 -0
  52. package/src/logo.svg +1 -0
  53. package/src/my_json_data.js +4146 -0
  54. package/src/settings.ts +6 -0
  55. package/src/setupTests.ts +1 -0
  56. package/src/utils/ErrorBoundary.tsx +41 -0
  57. package/src/utils/PopupButton.tsx +135 -0
  58. package/src/utils/activity.ts +8 -0
  59. package/src/utils/breakpoints.ts +5 -0
  60. package/src/utils/clipboard.ts +11 -0
  61. package/src/utils/test-utils.tsx +65 -0
  62. package/src/utils/useFullscreen.ts +65 -0
  63. package/src/vite-env.d.ts +1 -0
  64. package/tsconfig.json +27 -0
  65. package/tsconfig.node.json +9 -0
  66. package/vite.config.ts +17 -0
@@ -0,0 +1,219 @@
1
+ import { Button } from "primereact/button";
2
+ import { Panel } from "primereact/panel";
3
+ import styles from "./style.module.scss";
4
+ import { useAppDispatch, useAppSelector } from "../../app/hooks";
5
+ import {
6
+ selectIsGradingVisible,
7
+ selectOrientation,
8
+ selectPedagoTab,
9
+ toggleIsGradingVisible,
10
+ toggleIsPedagoVisible,
11
+ } from "../layout/layoutSlice";
12
+
13
+ import "@capytale/capytale-rich-text-editor/style.css";
14
+ import { Splitter, SplitterPanel } from "primereact/splitter";
15
+ import { classNames } from "primereact/utils";
16
+ import InstructionsEditor from "./InstructionsEditor";
17
+ import {
18
+ selectComments,
19
+ selectGrading,
20
+ selectHasGradingOrComments,
21
+ selectMode,
22
+ setComments,
23
+ setGrading,
24
+ } from "../activityData/activityDataSlice";
25
+ import { ChangeEventHandler } from "react";
26
+ import settings from "../../settings";
27
+ import { DivProps } from "react-html-props";
28
+ import AnswerSheetEditor from "./AnswerSheetEditor";
29
+
30
+ const Pedago: React.FC<DivProps> = ({ className, ...props }) => {
31
+ const dispatch = useAppDispatch();
32
+ const mode = useAppSelector(selectMode);
33
+ const comments = useAppSelector(selectComments);
34
+ const grading = useAppSelector(selectGrading);
35
+ const pedagoTab = useAppSelector(selectPedagoTab);
36
+ const hasGradingOrComments = useAppSelector(selectHasGradingOrComments);
37
+ const isGradingVisible =
38
+ useAppSelector(selectIsGradingVisible) &&
39
+ (hasGradingOrComments || mode === "review");
40
+ const isHorizontal = useAppSelector(selectOrientation) === "horizontal";
41
+ const mayReverse = isHorizontal
42
+ ? (tab: Array<any>) => tab.toReversed()
43
+ : (tab: Array<any>) => tab;
44
+
45
+ const handleCommentsChange: ChangeEventHandler<HTMLTextAreaElement> = (
46
+ event,
47
+ ) => {
48
+ console.log("Comments change:", event.target.value);
49
+ dispatch(setComments(event.target.value));
50
+ };
51
+
52
+ const handleGradingChange: ChangeEventHandler<HTMLTextAreaElement> = (
53
+ event,
54
+ ) => {
55
+ console.log("Grading change:", event.target.value);
56
+ dispatch(setGrading(event.target.value));
57
+ };
58
+
59
+ return (
60
+ // @ts-ignore - Incompatibility for props in TS
61
+ <div className={classNames(styles.pedago, className)} {...props}>
62
+ <div className={styles.pedagoCommands}>
63
+ <Button
64
+ severity="secondary"
65
+ icon="pi pi-times"
66
+ rounded
67
+ text
68
+ aria-label="Masquer les consignes"
69
+ tooltip="Masquer les consignes"
70
+ tooltipOptions={{
71
+ position: "left",
72
+ showDelay: settings.TOOLTIP_SHOW_DELAY,
73
+ }}
74
+ onClick={() => dispatch(toggleIsPedagoVisible())}
75
+ />
76
+ {/*
77
+ <Button
78
+ rounded
79
+ text
80
+ icon="pi pi-times"
81
+ label="H"
82
+ onClick={() => {
83
+ dispatch(
84
+ setPedagoTab(
85
+ pedagoTab === "instructions" ? "answerSheet" : "instructions",
86
+ ),
87
+ );
88
+ }}
89
+ />
90
+ */}
91
+ <div className={styles.pedagoSeparator}></div>
92
+ {(mode === "assignment" || mode === "review") && (
93
+ <ShowHideGradingButton />
94
+ )}
95
+ </div>
96
+ <div
97
+ className={styles.pedagoContent}
98
+ data-grading-visible={isGradingVisible}
99
+ >
100
+ {!isGradingVisible && (
101
+ <>
102
+ {pedagoTab === "instructions" && <InstructionsEditor />}
103
+ {pedagoTab === "answerSheet" && <AnswerSheetEditor />}
104
+ </>
105
+ )}
106
+ {isGradingVisible && (
107
+ <Splitter
108
+ layout={isHorizontal ? "horizontal" : "vertical"}
109
+ className={styles.pedagoSplitter}
110
+ >
111
+ {mayReverse([
112
+ <SplitterPanel
113
+ key="gradingPanel"
114
+ minSize={30}
115
+ size={40}
116
+ className={styles.gradingPanel}
117
+ >
118
+ <Panel
119
+ className={classNames(
120
+ styles.fullSizePanel,
121
+ styles.pedagoFeedbackPanel,
122
+ )}
123
+ header="Appréciation"
124
+ >
125
+ <div className={styles.pedagoFeedback}>
126
+ {(comments || mode !== "assignment") && (
127
+ <textarea
128
+ value={comments || ""}
129
+ placeholder={
130
+ mode !== "review" ? "" : "Rédigez ici l'appréciation."
131
+ }
132
+ id="comments"
133
+ name="comments"
134
+ rows={2}
135
+ readOnly={mode !== "review"}
136
+ onChange={handleCommentsChange}
137
+ className={styles.fullTextarea}
138
+ />
139
+ )}
140
+ </div>
141
+ </Panel>
142
+ <Panel
143
+ className={classNames(
144
+ styles.fullSizePanel,
145
+ styles.pedagoGradePanel,
146
+ )}
147
+ header="Évaluation"
148
+ >
149
+ <div className={styles.pedagoGrade}>
150
+ {(grading || mode !== "assignment") && (
151
+ <textarea
152
+ value={grading || ""}
153
+ placeholder={
154
+ mode !== "review"
155
+ ? ""
156
+ : "Écrivez ici l'évaluation libre (chiffrée ou non)."
157
+ }
158
+ id="grading"
159
+ name="grading"
160
+ rows={1}
161
+ readOnly={mode !== "review"}
162
+ onChange={handleGradingChange}
163
+ className={styles.fullTextarea}
164
+ />
165
+ )}
166
+ </div>
167
+ </Panel>
168
+ </SplitterPanel>,
169
+ <SplitterPanel
170
+ key="pedagoPanel"
171
+ minSize={50}
172
+ size={60}
173
+ className={styles.pedagoPanel}
174
+ >
175
+ <Panel className={styles.fullSizePanel} header="Consignes">
176
+ {pedagoTab === "instructions" && <InstructionsEditor />}
177
+ {pedagoTab === "answerSheet" && <AnswerSheetEditor />}
178
+ </Panel>
179
+ </SplitterPanel>
180
+ ])}
181
+ </Splitter>
182
+ )}
183
+ </div>
184
+ </div>
185
+ );
186
+ };
187
+
188
+ const ShowHideGradingButton: React.FC = () => {
189
+ const dispatch = useAppDispatch();
190
+ const mode = useAppSelector(selectMode);
191
+ const hasGradingOrComments = useAppSelector(selectHasGradingOrComments);
192
+ const isGradingVisible = useAppSelector(selectIsGradingVisible);
193
+ const tooltip =
194
+ hasGradingOrComments || mode === "review"
195
+ ? isGradingVisible
196
+ ? "Masquer la notation"
197
+ : "Afficher la notation"
198
+ : "Pas de note";
199
+
200
+ return (
201
+ <Button
202
+ severity="secondary"
203
+ icon="pi pi-graduation-cap"
204
+ disabled={!(hasGradingOrComments || mode === "review")}
205
+ rounded
206
+ text={!isGradingVisible || !(hasGradingOrComments || mode === "review")}
207
+ aria-label={tooltip}
208
+ tooltip={tooltip}
209
+ tooltipOptions={{
210
+ position: "left",
211
+ showDelay: settings.TOOLTIP_SHOW_DELAY,
212
+ showOnDisabled: true,
213
+ }}
214
+ onClick={() => dispatch(toggleIsGradingVisible())}
215
+ />
216
+ );
217
+ };
218
+
219
+ export default Pedago;
@@ -0,0 +1,104 @@
1
+ .pedago {
2
+ display: grid;
3
+ height: 100%;
4
+ width: 100%;
5
+ :global(.layout-horizontal) & {
6
+ grid-template-columns: 1fr calc(3rem + 8px);
7
+ grid-template-rows: 1fr;
8
+ }
9
+ :global(.layout-vertical) & {
10
+ grid-template-rows: calc(3rem + 8px) 1fr;
11
+ grid-template-columns: 1fr;
12
+ }
13
+ }
14
+
15
+ .pedagoCommands {
16
+ flex-direction: column;
17
+ padding: 4px;
18
+ display: flex;
19
+ gap: 8px;
20
+ background-color: var(--surface-200);
21
+ :global(.layout-horizontal) & {
22
+ grid-column-start: 2;
23
+ grid-row-start: 1;
24
+ }
25
+ :global(.layout-vertical) & {
26
+ flex-direction: row-reverse;
27
+ }
28
+ }
29
+
30
+ .pedagoSeparator {
31
+ flex-grow: 1;
32
+ }
33
+
34
+ .pedagoSplitter {
35
+ height: 100%;
36
+ border: none;
37
+ border-radius: 0;
38
+ background-color: var(--surface-0);
39
+ & > :global(.p-splitter-panel) {
40
+ overflow-y: hidden;
41
+ }
42
+ }
43
+
44
+ .gradingPanel {
45
+ display: flex;
46
+ gap: 4px;
47
+ }
48
+
49
+ *:has(> .fullSizePanel) {
50
+ padding: 4px;
51
+ }
52
+
53
+ .fullSizePanel {
54
+ width: 100%;
55
+ height: 100%;
56
+ display: flex;
57
+ flex-direction: column;
58
+ & > :global(.p-panel-header) {
59
+ flex-shrink: 0;
60
+ }
61
+ & > :global(.p-toggleable-content) {
62
+ flex-grow: 1;
63
+ overflow: hidden;
64
+ }
65
+ & > :global(.p-toggleable-content) > * {
66
+ height: 100%;
67
+ padding: 0;
68
+ overflow: auto;
69
+ display: flex;
70
+ }
71
+ }
72
+
73
+ .pedagoContent {
74
+ overflow-y: auto;
75
+ :global(.layout-horizontal) & {
76
+ grid-column-start: 1;
77
+ grid-row-start: 1;
78
+ }
79
+ }
80
+
81
+ .pedagoFeedbackPanel {
82
+ flex-grow: 3;
83
+ flex-basis: 0;
84
+ }
85
+
86
+ .pedagoGradePanel {
87
+ flex-grow: 2;
88
+ flex-basis: 0;
89
+ }
90
+
91
+ .fullTextarea {
92
+ width: 100%;
93
+ padding: 0.7em;
94
+ border: 1px solid transparent;
95
+ resize: none;
96
+ height: 100%;
97
+ background-color: transparent;
98
+ }
99
+
100
+ *:has(> .fullTextarea) {
101
+ height: 100%;
102
+ width: 100%;
103
+ overflow: hidden;
104
+ }
@@ -0,0 +1,51 @@
1
+ import { FC, useCallback, useContext, useEffect, useMemo } from "react";
2
+ import { PrimeReactContext } from "primereact/api";
3
+ import { useAppDispatch, useAppSelector } from "../../app/hooks";
4
+ import {
5
+ selectAutoSwitch,
6
+ selectPreviousThemePath,
7
+ selectThemePath,
8
+ switchToTheme,
9
+ } from "./themingSlice";
10
+
11
+ const ThemeSwitcher: FC = () => {
12
+ const dispatch = useAppDispatch();
13
+ const mediaMatch = useMemo(
14
+ () => window.matchMedia("(prefers-color-scheme: dark)"),
15
+ [],
16
+ );
17
+ const { changeTheme } = useContext(PrimeReactContext);
18
+ const themePath = useAppSelector(selectThemePath);
19
+ const previousThemePath = useAppSelector(selectPreviousThemePath);
20
+ const autoSwitch = useAppSelector(selectAutoSwitch);
21
+ const handlePrefersColorScheme = useCallback((e: MediaQueryListEvent) => {
22
+ dispatch(switchToTheme(e.matches ? "dark" : "light"));
23
+ }, []);
24
+ useEffect(() => {
25
+ if (themePath !== previousThemePath) {
26
+ changeTheme &&
27
+ changeTheme(
28
+ previousThemePath as string,
29
+ themePath as string,
30
+ "theme-link",
31
+ );
32
+ }
33
+ }, [themePath]);
34
+ useEffect(() => {
35
+ if (autoSwitch) {
36
+ if (mediaMatch.matches) {
37
+ dispatch(switchToTheme("dark"));
38
+ } else {
39
+ dispatch(switchToTheme("light"));
40
+ }
41
+ mediaMatch.addEventListener("change", handlePrefersColorScheme);
42
+ return () => {
43
+ mediaMatch.removeEventListener("change", handlePrefersColorScheme);
44
+ };
45
+ }
46
+ }, [autoSwitch]);
47
+
48
+ return <></>;
49
+ };
50
+
51
+ export default ThemeSwitcher;
@@ -0,0 +1,93 @@
1
+ import type { PayloadAction } from "@reduxjs/toolkit";
2
+ import { createAppSlice } from "../../app/createAppSlice";
3
+
4
+ type ThemeData = {
5
+ id: string;
6
+ name: string;
7
+ path: string;
8
+ isDark: boolean;
9
+ };
10
+
11
+ export interface ThemingState {
12
+ themes: ThemeData[];
13
+ currentTheme: number;
14
+ previousTheme: number;
15
+ autoSwitch: boolean;
16
+ }
17
+
18
+ const initialState: ThemingState = {
19
+ themes: [
20
+ {
21
+ id: "light",
22
+ name: "Clair",
23
+ path: "https://cdn.ac-paris.fr/capytale/meta-player/themes/lara-light-blue/theme.css",
24
+ isDark: false,
25
+ },
26
+ {
27
+ id: "dark",
28
+ name: "Sombre",
29
+ path: "https://cdn.ac-paris.fr/capytale/meta-player/themes/lara-dark-blue/theme.css",
30
+ isDark: true,
31
+ },
32
+ ],
33
+ currentTheme: 0,
34
+ previousTheme: 0,
35
+ autoSwitch: false,
36
+ };
37
+
38
+ // If you are not using async thunks you can use the standalone `createSlice`.
39
+ export const themingSlice = createAppSlice({
40
+ name: "theming",
41
+ // `createSlice` will infer the state type from the `initialState` argument
42
+ initialState,
43
+ // The `reducers` field lets us define reducers and generate associated actions
44
+ reducers: (create) => ({
45
+ switchToNextTheme: create.reducer((state) => {
46
+ // Redux Toolkit allows us to write "mutating" logic in reducers. It
47
+ // doesn't actually mutate the state because it uses the Immer library,
48
+ // which detects changes to a "draft state" and produces a brand new
49
+ // immutable state based off those changes
50
+ state.previousTheme = state.currentTheme;
51
+ state.currentTheme = (state.currentTheme + 1) % state.themes.length;
52
+ }),
53
+ // Use the `PayloadAction` type to declare the contents of `action.payload`
54
+ switchToTheme: create.reducer((state, action: PayloadAction<string>) => {
55
+ state.previousTheme = state.currentTheme;
56
+ state.currentTheme = state.themes.findIndex(
57
+ (theme) => theme.id === action.payload,
58
+ );
59
+ }),
60
+ setAutoSwitch: create.reducer((state, action: PayloadAction<boolean>) => {
61
+ state.autoSwitch = action.payload;
62
+ }),
63
+ }),
64
+ // You can define your selectors here. These selectors receive the slice
65
+ // state as their first argument.
66
+ selectors: {
67
+ selectThemePath: (theming) => theming.themes[theming.currentTheme].path,
68
+ selectPreviousThemePath: (theming) =>
69
+ theming.themes[theming.previousTheme].path,
70
+ selectThemeName: (theming) => theming.themes[theming.currentTheme].name,
71
+ selectThemeIsDark: (theming) => theming.themes[theming.currentTheme].isDark,
72
+ selectAutoSwitch: (theming) => theming.autoSwitch,
73
+ selectThemeNameOrAuto: (theming) => {
74
+ return theming.autoSwitch
75
+ ? "Système (auto)"
76
+ : theming.themes[theming.currentTheme].name;
77
+ },
78
+ },
79
+ });
80
+
81
+ // Action creators are generated for each case reducer function.
82
+ export const { switchToNextTheme, switchToTheme, setAutoSwitch } =
83
+ themingSlice.actions;
84
+
85
+ // Selectors returned by `slice.selectors` take the root state as their first argument.
86
+ export const {
87
+ selectThemePath,
88
+ selectPreviousThemePath,
89
+ selectThemeName,
90
+ selectThemeIsDark,
91
+ selectAutoSwitch,
92
+ selectThemeNameOrAuto,
93
+ } = themingSlice.selectors;
@@ -0,0 +1,8 @@
1
+ import { ActivityMode } from "@capytale/activity.js/activity/activitySession";
2
+ import { useAppSelector } from "../app/hooks";
3
+ import { selectMode } from "../features/activityData/activityDataSlice";
4
+
5
+ export const useMode = () => {
6
+ const mode: ActivityMode = useAppSelector(selectMode);
7
+ return mode;
8
+ };
package/src/index.css ADDED
@@ -0,0 +1,30 @@
1
+ body {
2
+ margin: 0;
3
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
4
+ "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
5
+ sans-serif;
6
+ -webkit-font-smoothing: antialiased;
7
+ -moz-osx-font-smoothing: grayscale;
8
+ }
9
+
10
+ code {
11
+ font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
12
+ monospace;
13
+ }
14
+
15
+ html, body, #root {
16
+ height: 100%;
17
+ }
18
+
19
+ .p-splitter-gutter {
20
+ background-color: var(--surface-200);
21
+ }
22
+
23
+ .p-splitter-gutter-handle, .p-splitter-gutter-resizing {
24
+ background-color: var(--surface-400);
25
+ }
26
+
27
+ *:has(>.editor-shell) { /* Rich text editor */
28
+ background-color: white;
29
+ color-scheme: light;
30
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,6 @@
1
+ import "primeicons/primeicons.css";
2
+ import "./index.css";
3
+
4
+ import MetaPlayer from "./MetaPlayer";
5
+
6
+ export { MetaPlayer };
package/src/logo.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><g fill="#764ABC"><path d="M65.6 65.4c2.9-.3 5.1-2.8 5-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 1.5.7 2.8 1.6 3.7-3.4 6.7-8.6 11.6-16.4 15.7-5.3 2.8-10.8 3.8-16.3 3.1-4.5-.6-8-2.6-10.2-5.9-3.2-4.9-3.5-10.2-.8-15.5 1.9-3.8 4.9-6.6 6.8-8-.4-1.3-1-3.5-1.3-5.1-14.5 10.5-13 24.7-8.6 31.4 3.3 5 10 8.1 17.4 8.1 2 0 4-.2 6-.7 12.8-2.5 22.5-10.1 28-21.4z"/><path d="M83.2 53c-7.6-8.9-18.8-13.8-31.6-13.8H50c-.9-1.8-2.8-3-4.9-3h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 3 2.6 5.4 5.6 5.4h.2c2.2-.1 4.1-1.5 4.9-3.4H52c7.6 0 14.8 2.2 21.3 6.5 5 3.3 8.6 7.6 10.6 12.8 1.7 4.2 1.6 8.3-.2 11.8-2.8 5.3-7.5 8.2-13.7 8.2-4 0-7.8-1.2-9.8-2.1-1.1 1-3.1 2.6-4.5 3.6 4.3 2 8.7 3.1 12.9 3.1 9.6 0 16.7-5.3 19.4-10.6 2.9-5.8 2.7-15.8-4.8-24.3z"/><path d="M32.4 67.1c.1 3 2.6 5.4 5.6 5.4h.2c3.1-.1 5.5-2.7 5.4-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-.2 0-.5 0-.7.1-4.1-6.8-5.8-14.2-5.2-22.2.4-6 2.4-11.2 5.9-15.5 2.9-3.7 8.5-5.5 12.3-5.6 10.6-.2 15.1 13 15.4 18.3 1.3.3 3.5 1 5 1.5-1.2-16.2-11.2-24.6-20.8-24.6-9 0-17.3 6.5-20.6 16.1-4.6 12.8-1.6 25.1 4 34.8-.5.7-.8 1.8-.7 2.9z"/></g></svg>