@jvs-milkdown/crepe 1.2.0 → 1.2.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 (75) hide show
  1. package/lib/cjs/builder.js +55 -2
  2. package/lib/cjs/builder.js.map +1 -1
  3. package/lib/cjs/feature/block-edit/index.js +73 -5
  4. package/lib/cjs/feature/block-edit/index.js.map +1 -1
  5. package/lib/cjs/feature/code-mirror/index.js +65 -3
  6. package/lib/cjs/feature/code-mirror/index.js.map +1 -1
  7. package/lib/cjs/feature/cursor/index.js +55 -2
  8. package/lib/cjs/feature/cursor/index.js.map +1 -1
  9. package/lib/cjs/feature/image-block/index.js +76 -8
  10. package/lib/cjs/feature/image-block/index.js.map +1 -1
  11. package/lib/cjs/feature/latex/index.js +55 -2
  12. package/lib/cjs/feature/latex/index.js.map +1 -1
  13. package/lib/cjs/feature/link-tooltip/index.js +55 -2
  14. package/lib/cjs/feature/link-tooltip/index.js.map +1 -1
  15. package/lib/cjs/feature/list-item/index.js +55 -2
  16. package/lib/cjs/feature/list-item/index.js.map +1 -1
  17. package/lib/cjs/feature/placeholder/index.js +55 -2
  18. package/lib/cjs/feature/placeholder/index.js.map +1 -1
  19. package/lib/cjs/feature/table/index.js +55 -2
  20. package/lib/cjs/feature/table/index.js.map +1 -1
  21. package/lib/cjs/feature/toolbar/index.js +73 -5
  22. package/lib/cjs/feature/toolbar/index.js.map +1 -1
  23. package/lib/cjs/index.js +451 -30
  24. package/lib/cjs/index.js.map +1 -1
  25. package/lib/esm/builder.js +55 -2
  26. package/lib/esm/builder.js.map +1 -1
  27. package/lib/esm/feature/block-edit/index.js +73 -5
  28. package/lib/esm/feature/block-edit/index.js.map +1 -1
  29. package/lib/esm/feature/code-mirror/index.js +65 -3
  30. package/lib/esm/feature/code-mirror/index.js.map +1 -1
  31. package/lib/esm/feature/cursor/index.js +55 -2
  32. package/lib/esm/feature/cursor/index.js.map +1 -1
  33. package/lib/esm/feature/image-block/index.js +76 -8
  34. package/lib/esm/feature/image-block/index.js.map +1 -1
  35. package/lib/esm/feature/latex/index.js +55 -2
  36. package/lib/esm/feature/latex/index.js.map +1 -1
  37. package/lib/esm/feature/link-tooltip/index.js +55 -2
  38. package/lib/esm/feature/link-tooltip/index.js.map +1 -1
  39. package/lib/esm/feature/list-item/index.js +55 -2
  40. package/lib/esm/feature/list-item/index.js.map +1 -1
  41. package/lib/esm/feature/placeholder/index.js +55 -2
  42. package/lib/esm/feature/placeholder/index.js.map +1 -1
  43. package/lib/esm/feature/table/index.js +55 -2
  44. package/lib/esm/feature/table/index.js.map +1 -1
  45. package/lib/esm/feature/toolbar/index.js +73 -5
  46. package/lib/esm/feature/toolbar/index.js.map +1 -1
  47. package/lib/esm/index.js +452 -31
  48. package/lib/esm/index.js.map +1 -1
  49. package/lib/theme/common/code-mirror.css +13 -0
  50. package/lib/theme/common/image-block.css +321 -22
  51. package/lib/theme/common/reset.css +3 -0
  52. package/lib/theme/common/toolbar.css +27 -2
  53. package/lib/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +4 -4
  55. package/src/core/locale.ts +55 -0
  56. package/src/feature/block-edit/menu/config.ts +19 -3
  57. package/src/feature/code-mirror/index.ts +3 -0
  58. package/src/feature/fixed-toolbar/component.tsx +31 -2
  59. package/src/feature/fixed-toolbar/config.ts +18 -3
  60. package/src/feature/fixed-toolbar/document-header.tsx +8 -4
  61. package/src/feature/fixed-toolbar/index.ts +18 -1
  62. package/src/feature/fixed-toolbar/menu-bar.tsx +83 -2
  63. package/src/feature/fixed-toolbar/shortcut-help-modal.tsx +244 -0
  64. package/src/feature/fixed-toolbar/view-menu-state.ts +9 -0
  65. package/src/feature/image-block/index.ts +11 -1
  66. package/src/icons/border.ts +5 -0
  67. package/src/icons/close.ts +3 -0
  68. package/src/icons/crop.ts +6 -0
  69. package/src/icons/index.ts +5 -0
  70. package/src/icons/keyboard.ts +3 -0
  71. package/src/icons/word-wrap.ts +6 -0
  72. package/src/theme/common/code-mirror.css +13 -0
  73. package/src/theme/common/image-block.css +364 -23
  74. package/src/theme/common/reset.css +3 -0
  75. package/src/theme/common/toolbar.css +30 -2
@@ -0,0 +1,244 @@
1
+ import type { Ctx } from '@jvs-milkdown/kit/ctx'
2
+
3
+ import { Icon } from '@jvs-milkdown/kit/component'
4
+ import { defineComponent, type Ref, h, onMounted, onBeforeUnmount } from 'vue'
5
+
6
+ import type { CrepeTranslations } from '../../core/locale'
7
+
8
+ import { i18n } from '../../core/locale'
9
+ import { closeIcon } from '../../icons'
10
+
11
+ const isMac =
12
+ typeof navigator !== 'undefined' &&
13
+ /Mac|iPod|iPhone|iPad/.test(navigator.platform || navigator.userAgent)
14
+
15
+ function fmt(shortcut: string): string {
16
+ return shortcut
17
+ .replace(/Mod/g, isMac ? '⌘' : 'Ctrl')
18
+ .replace(/\+/g, isMac ? '' : '+')
19
+ .replace(/Shift/g, isMac ? '⇧' : 'Shift')
20
+ .replace(/Alt/g, isMac ? '⌥' : 'Alt')
21
+ }
22
+
23
+ type ShortcutEntry = { labelKey: keyof CrepeTranslations; keys: string[] }
24
+ type ShortcutGroup = {
25
+ titleKey: keyof CrepeTranslations
26
+ items: ShortcutEntry[]
27
+ }
28
+
29
+ const leftGroups: ShortcutGroup[] = [
30
+ {
31
+ titleKey: 'shortcuts.formatting',
32
+ items: [
33
+ { labelKey: 'shortcuts.bold', keys: ['Mod+b'] },
34
+ { labelKey: 'shortcuts.italic', keys: ['Mod+i'] },
35
+ { labelKey: 'shortcuts.underline', keys: ['Mod+u'] },
36
+ { labelKey: 'shortcuts.strikethrough', keys: ['Mod+Alt+x'] },
37
+ { labelKey: 'shortcuts.inlineCode', keys: ['Mod+e'] },
38
+ { labelKey: 'shortcuts.highlight', keys: ['Mod+Shift+h'] },
39
+ ],
40
+ },
41
+ {
42
+ titleKey: 'shortcuts.paragraph',
43
+ items: [
44
+ { labelKey: 'shortcuts.paragraph', keys: ['Mod+Alt+0'] },
45
+ { labelKey: 'shortcuts.heading', keys: ['Mod+Alt+1~6'] },
46
+ ],
47
+ },
48
+ ]
49
+
50
+ const rightGroups: ShortcutGroup[] = [
51
+ {
52
+ titleKey: 'shortcuts.block',
53
+ items: [
54
+ { labelKey: 'shortcuts.codeBlock', keys: ['Mod+Alt+c'] },
55
+ { labelKey: 'shortcuts.blockquote', keys: ['Mod+Shift+b'] },
56
+ { labelKey: 'shortcuts.orderedList', keys: ['Mod+Alt+7'] },
57
+ { labelKey: 'shortcuts.bulletList', keys: ['Mod+Alt+8'] },
58
+ { labelKey: 'shortcuts.indent', keys: ['Tab', 'Mod+]'] },
59
+ { labelKey: 'shortcuts.outdent', keys: ['Shift+Tab', 'Mod+['] },
60
+ { labelKey: 'shortcuts.hardBreak', keys: ['Shift+Enter'] },
61
+ ],
62
+ },
63
+ {
64
+ titleKey: 'shortcuts.history',
65
+ items: [
66
+ { labelKey: 'shortcuts.undo', keys: ['Mod+z'] },
67
+ { labelKey: 'shortcuts.redo', keys: ['Mod+y', 'Shift+Mod+z'] },
68
+ ],
69
+ },
70
+ ]
71
+
72
+ type ModalProps = {
73
+ ctx: Ctx
74
+ visible: Ref<boolean>
75
+ }
76
+
77
+ const badgeStyle = {
78
+ display: 'inline-flex',
79
+ alignItems: 'center',
80
+ justifyContent: 'center',
81
+ minWidth: '22px',
82
+ height: '22px',
83
+ padding: '0 6px',
84
+ borderRadius: '4px',
85
+ fontSize: '11px',
86
+ fontFamily: 'var(--crepe-font-code, monospace)',
87
+ backgroundColor:
88
+ 'var(--crepe-color-surface-low, color-mix(in srgb, var(--crepe-color-surface), transparent 60%))',
89
+ border:
90
+ '1px solid var(--crepe-color-outline-variant, color-mix(in srgb, var(--crepe-color-outline), transparent 80%))',
91
+ whiteSpace: 'nowrap' as const,
92
+ }
93
+
94
+ const columnStyle = {
95
+ flex: 1,
96
+ minWidth: 0,
97
+ }
98
+
99
+ const groupTitleStyle = {
100
+ fontSize: '11px',
101
+ fontWeight: 600,
102
+ color: 'var(--crepe-color-on-surface-variant, #888)',
103
+ marginBottom: '8px',
104
+ textTransform: 'uppercase' as const,
105
+ letterSpacing: '0.5px',
106
+ }
107
+
108
+ const rowStyle = {
109
+ display: 'flex',
110
+ alignItems: 'center',
111
+ justifyContent: 'space-between',
112
+ padding: '5px 0',
113
+ }
114
+
115
+ const labelStyle = {
116
+ fontSize: '13px',
117
+ color: 'var(--crepe-color-on-surface, #333)',
118
+ }
119
+
120
+ function renderGroup(ctx: Ctx, group: ShortcutGroup) {
121
+ return (
122
+ <div style={{ marginBottom: '16px' }} key={group.titleKey}>
123
+ <div style={groupTitleStyle}>{i18n(ctx, group.titleKey)}</div>
124
+ <div style={{ display: 'flex', flexDirection: 'column' }}>
125
+ {group.items.map((item) => (
126
+ <div key={item.labelKey} style={rowStyle}>
127
+ <span style={labelStyle}>{i18n(ctx, item.labelKey)}</span>
128
+ <div style={{ display: 'flex', gap: '4px', alignItems: 'center' }}>
129
+ {item.keys.map((key, i) => (
130
+ <span key={i} style={badgeStyle}>
131
+ {fmt(key)}
132
+ </span>
133
+ ))}
134
+ </div>
135
+ </div>
136
+ ))}
137
+ </div>
138
+ </div>
139
+ )
140
+ }
141
+
142
+ export const ShortcutHelpModal = defineComponent<ModalProps>({
143
+ props: {
144
+ ctx: { type: Object, required: true },
145
+ visible: { type: Object, required: true },
146
+ },
147
+ setup(props) {
148
+ const close = () => {
149
+ props.visible.value = false
150
+ }
151
+
152
+ const onKeydown = (e: KeyboardEvent) => {
153
+ if (e.key === 'Escape') close()
154
+ }
155
+
156
+ onMounted(() => document.addEventListener('keydown', onKeydown))
157
+ onBeforeUnmount(() => document.removeEventListener('keydown', onKeydown))
158
+
159
+ return () => (
160
+ <div
161
+ style={{
162
+ position: 'fixed',
163
+ inset: 0,
164
+ zIndex: 10000,
165
+ background: 'rgba(0, 0, 0, 0.4)',
166
+ display: 'flex',
167
+ alignItems: 'center',
168
+ justifyContent: 'center',
169
+ }}
170
+ onClick={close}
171
+ >
172
+ <div
173
+ style={{
174
+ position: 'relative',
175
+ width: '560px',
176
+ maxWidth: 'calc(100vw - 32px)',
177
+ background: 'var(--crepe-color-surface, #fff)',
178
+ borderRadius: '12px',
179
+ boxShadow: '0 8px 32px rgba(0, 0, 0, 0.15)',
180
+ padding: '20px 24px',
181
+ }}
182
+ onClick={(e) => e.stopPropagation()}
183
+ >
184
+ <div
185
+ style={{
186
+ display: 'flex',
187
+ alignItems: 'center',
188
+ justifyContent: 'space-between',
189
+ marginBottom: '16px',
190
+ paddingBottom: '12px',
191
+ borderBottom:
192
+ '1px solid var(--crepe-color-outline-variant, color-mix(in srgb, var(--crepe-color-outline), transparent 80%))',
193
+ }}
194
+ >
195
+ <span
196
+ style={{
197
+ fontSize: '15px',
198
+ fontWeight: 600,
199
+ color: 'var(--crepe-color-on-surface, #1a1a1a)',
200
+ }}
201
+ >
202
+ {i18n(props.ctx, 'shortcuts.title')}
203
+ </span>
204
+ <button
205
+ type="button"
206
+ onClick={close}
207
+ style={{
208
+ display: 'inline-flex',
209
+ alignItems: 'center',
210
+ justifyContent: 'center',
211
+ width: '28px',
212
+ height: '28px',
213
+ border: 'none',
214
+ background: 'transparent',
215
+ cursor: 'pointer',
216
+ borderRadius: '4px',
217
+ color: 'var(--crepe-color-on-surface-variant, #666)',
218
+ }}
219
+ >
220
+ <Icon icon={closeIcon} />
221
+ </button>
222
+ </div>
223
+
224
+ <div style={{ display: 'flex', gap: '24px' }}>
225
+ <div style={columnStyle}>
226
+ {leftGroups.map((g) => renderGroup(props.ctx, g))}
227
+ </div>
228
+ <div
229
+ style={{
230
+ width: '1px',
231
+ flexShrink: 0,
232
+ backgroundColor:
233
+ 'var(--crepe-color-outline-variant, color-mix(in srgb, var(--crepe-color-outline), transparent 80%))',
234
+ }}
235
+ />
236
+ <div style={columnStyle}>
237
+ {rightGroups.map((g) => renderGroup(props.ctx, g))}
238
+ </div>
239
+ </div>
240
+ </div>
241
+ </div>
242
+ )
243
+ },
244
+ })
@@ -1,6 +1,7 @@
1
1
  import { reactive } from 'vue'
2
2
 
3
3
  export type OutlinePosition = 'left' | 'right'
4
+ export type EditorWidth = 'default' | 'wide' | 'full'
4
5
 
5
6
  export interface ViewMenuState {
6
7
  outlineVisible: boolean
@@ -10,6 +11,13 @@ export interface ViewMenuState {
10
11
  showTitle: boolean
11
12
  showCover: boolean
12
13
  coverUrl: string
14
+ editorWidth: EditorWidth
15
+ }
16
+
17
+ export const editorWidthMap: Record<EditorWidth, string> = {
18
+ default: '820px',
19
+ wide: '1020px',
20
+ full: 'none',
13
21
  }
14
22
 
15
23
  export const createViewMenuState = () =>
@@ -21,6 +29,7 @@ export const createViewMenuState = () =>
21
29
  showTitle: false,
22
30
  showCover: false,
23
31
  coverUrl: '',
32
+ editorWidth: 'default',
24
33
  })
25
34
 
26
35
  import { $ctx } from '@jvs-milkdown/kit/utils'
@@ -16,7 +16,13 @@ import type { DefineFeature } from '../shared'
16
16
 
17
17
  import { i18n } from '../../core/locale'
18
18
  import { crepeFeatureConfig } from '../../core/slice'
19
- import { captionIcon, imageIcon, confirmIcon } from '../../icons'
19
+ import {
20
+ captionIcon,
21
+ imageIcon,
22
+ confirmIcon,
23
+ cropIcon,
24
+ borderIcon,
25
+ } from '../../icons'
20
26
  import { CrepeFeature } from '../index'
21
27
 
22
28
  interface ImageBlockConfig {
@@ -32,6 +38,8 @@ interface ImageBlockConfig {
32
38
  blockImageIcon: string
33
39
  blockConfirmButton: string
34
40
  blockCaptionIcon: string
41
+ blockCropIcon: string
42
+ blockBorderIcon: string
35
43
  blockUploadButton: string
36
44
  blockCaptionPlaceholderText: string
37
45
  blockUploadPlaceholderText: string
@@ -68,6 +76,8 @@ export const imageBlock: DefineFeature<ImageBlockFeatureConfig> = (
68
76
  i18n(ctx, 'imageBlock.blockUploadButton'),
69
77
  imageIcon: config?.blockImageIcon ?? imageIcon,
70
78
  captionIcon: config?.blockCaptionIcon ?? captionIcon,
79
+ cropIcon: config?.blockCropIcon ?? cropIcon,
80
+ borderIcon: config?.blockBorderIcon ?? borderIcon,
71
81
  confirmButton:
72
82
  config?.blockConfirmButton ??
73
83
  i18n(ctx, 'imageBlock.blockConfirmButton'),
@@ -0,0 +1,5 @@
1
+ export const borderIcon = `
2
+ <svg viewBox="0 0 1024 1024" height="24px" width="24px">
3
+ <path d="M160 160h704v704H160z" fill="none" stroke="currentColor" stroke-width="64" stroke-dasharray="64 32"/>
4
+ </svg>
5
+ `
@@ -0,0 +1,3 @@
1
+ export const closeIcon = `
2
+ <svg viewBox="0 0 1024 1024" width="20" height="20"><path d="M619.52 544.448l324.672-324.928a53.632 53.632 0 0 0 0-75.776 53.376 53.376 0 0 0-75.712 0L543.872 468.672l-324.48-324.928a53.376 53.376 0 0 0-75.712 0 53.632 53.632 0 0 0 0 75.776l324.48 324.8-324.48 324.928a53.632 53.632 0 0 0 75.776 75.776l324.416-324.8 324.48 324.8a53.184 53.184 0 0 0 37.952 15.744 53.696 53.696 0 0 0 37.76-91.52l-324.48-324.8z" fill="#6F7588"></path></svg>
3
+ `
@@ -0,0 +1,6 @@
1
+ export const cropIcon = `
2
+ <svg viewBox="0 0 1024 1024" height="24px" width="24px">
3
+ <path d="M192 128v128H128v64h192V128h-128z m0 0h64v128h-64V128z m512 0v128h192v64H640V128h64z m-512 640H128v-64h192v192h-128v-128z m0 128h64v-128h-64v128z m512 0v-128h192v64h-128v128h-64z m0 0h64v-128h-64v128z" fill="currentColor"/>
4
+ <path d="M256 256h512v512H256z" fill="none" stroke="currentColor" stroke-width="64"/>
5
+ </svg>
6
+ `
@@ -9,6 +9,7 @@ export * from './check-box-checked'
9
9
  export * from './check-box-unchecked'
10
10
  export * from './chevron-down'
11
11
  export * from './clear'
12
+ export * from './close'
12
13
  export * from './code'
13
14
  export * from './confirm'
14
15
  export * from './copy'
@@ -47,3 +48,7 @@ export * from './line-code'
47
48
  export * from './merge-cell'
48
49
  export * from './more'
49
50
  export * from './split-cell'
51
+ export * from './keyboard'
52
+ export * from './word-wrap'
53
+ export * from './crop'
54
+ export * from './border'
@@ -0,0 +1,3 @@
1
+ export const keyboardIcon = `
2
+ <svg t="1778036070440" class="icon" viewBox="0 0 1024 1024" width="20" height="20"><path d="M934.4 190.08c0-23.68-18.048-42.88-40.192-42.88H129.856c-22.208 0-40.256 19.2-40.256 42.88v643.84c0 23.68 17.984 42.88 40.32 42.88h764.288c22.144 0 40.128-19.2 40.192-42.88V190.08z m-764.352 43.008h683.968v558.016H170.048V233.088z m603.456 343.296c11.072 0 20.16 9.728 20.16 21.44v42.944c0 11.84-9.088 21.44-20.16 21.44H250.56a20.8 20.8 0 0 1-20.16-21.44v-42.88c0-11.904 9.088-21.504 20.16-21.504h522.88z m-422.4-193.088a20.8 20.8 0 0 0-20.096-21.44H250.56a20.8 20.8 0 0 0-20.16 21.44v42.88c0 11.904 9.088 21.504 20.16 21.504h80.448c11.072 0 20.096-9.728 20.096-21.44v-42.944zM512 361.856c11.136 0 20.096 9.6 20.096 21.44v42.88c0 11.84-8.96 21.504-20.096 21.504H431.616a20.8 20.8 0 0 1-20.16-21.44v-42.944c0-11.84 9.024-21.44 20.16-21.44H512z m281.6 21.44a20.8 20.8 0 0 0-20.096-21.44H612.608a20.8 20.8 0 0 0-20.16 21.44v42.88c0 11.904 9.088 21.504 20.16 21.504h160.896a20.928 20.928 0 0 0 20.16-21.44v-42.944z" fill="#363B4C" p-id="13200"></path></svg>
3
+ `
@@ -0,0 +1,6 @@
1
+ export const wordWrapIcon = `
2
+ <svg viewBox="0 0 1024 1024" height="24px"
3
+ width="24px">
4
+ <path d="M128 850.88V173.12q0-4.48 0.896-8.832 0.832-4.352 2.56-8.448 1.664-4.096 4.16-7.808 2.432-3.648 5.568-6.848 3.2-3.136 6.848-5.568 3.712-2.496 7.808-4.16 4.096-1.728 8.448-2.56Q168.704 128 173.12 128q4.48 0 8.832 0.896 4.352 0.832 8.448 2.56 4.096 1.664 7.808 4.16 3.648 2.432 6.848 5.568 3.136 3.2 5.568 6.848 2.496 3.712 4.16 7.808 1.728 4.096 2.56 8.448 0.896 4.416 0.896 8.832v677.76q0 4.48-0.896 8.768-0.832 4.416-2.56 8.512-1.664 4.096-4.16 7.808-2.432 3.648-5.568 6.848-3.2 3.136-6.848 5.568-3.712 2.496-7.808 4.16-4.096 1.728-8.448 2.56-4.416 0.896-8.832 0.896-4.48 0-8.832-0.896-4.352-0.832-8.448-2.56-4.096-1.664-7.808-4.16-3.648-2.432-6.848-5.632-3.136-3.136-5.568-6.784-2.496-3.712-4.16-7.808-1.728-4.096-2.56-8.512Q128 855.296 128 850.88z m677.76 0a45.12 45.12 0 0 0 90.24 0V173.12a45.12 45.12 0 0 0-90.24 0v677.76zM471.232 684.8a46.208 46.208 0 0 1-4.288 0.192h-48l48 48.448c8.832 8.896 13.056 20.288 13.056 32a44.8 44.8 0 0 1-12.992 32 45.184 45.184 0 0 1-64 0L282.24 676.224A44.992 44.992 0 0 1 269.184 640a44.992 44.992 0 0 1 13.056-36.288l120.704-120.704a45.184 45.184 0 1 1 64 64l-47.744 47.744h47.744c2.176 0 4.352 0.128 6.464 0.448h70.656q47.68 0 81.472-33.728 33.728-33.792 33.728-81.472 0-47.744-33.728-81.472Q591.744 364.8 544 364.8H384a44.8 44.8 0 1 1 0-89.6h160q84.8 0 144.832 59.968 59.968 59.968 59.968 144.832 0 84.8-59.968 144.832-60.032 59.968-144.832 59.968H471.232z" fill="#363B4C"></path>
5
+ </svg>
6
+ `
@@ -34,6 +34,10 @@
34
34
  background: transparent;
35
35
  }
36
36
 
37
+ .cm-activeLineGutter {
38
+ background-color: #f5f5f5;
39
+ }
40
+
37
41
  .cm-panel {
38
42
  font-family: var(--crepe-font-default);
39
43
  background: transparent;
@@ -140,6 +144,15 @@
140
144
  border-top-right-radius: 100px;
141
145
  border-bottom-right-radius: 100px;
142
146
  }
147
+
148
+ button.word-wrap-active {
149
+ background: var(--crepe-color-selected);
150
+ color: var(--crepe-color-primary);
151
+
152
+ svg {
153
+ stroke: var(--crepe-color-primary);
154
+ }
155
+ }
143
156
  }
144
157
 
145
158
  .language-button {