@budibase/bbui 3.35.3 → 3.35.10

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/bbui",
3
3
  "description": "A UI solution used in the different Budibase projects.",
4
- "version": "3.35.3",
4
+ "version": "3.35.10",
5
5
  "license": "MPL-2.0",
6
6
  "module": "dist/bbui.mjs",
7
7
  "exports": {
@@ -103,5 +103,5 @@
103
103
  }
104
104
  }
105
105
  },
106
- "gitHead": "d5b05b266b9b7726bdcd7a9e7affb3330129805c"
106
+ "gitHead": "e2fa586beb21f730b22350e561d21ec1e2c5c2f0"
107
107
  }
@@ -4,6 +4,7 @@
4
4
  import Icon from "../Icon/Icon.svelte"
5
5
  import { fade } from "svelte/transition"
6
6
  import { hexToRGBA } from "../helpers"
7
+ import ProgressCircle from "../ProgressCircle/ProgressCircle.svelte"
7
8
 
8
9
  export let quiet: boolean = false
9
10
  export let selected: boolean = false
@@ -15,10 +16,17 @@
15
16
  export let noPadding: boolean = false
16
17
  export let tooltip: string = ""
17
18
  export let accentColor: string | null = null
19
+ export let loading: boolean = false
18
20
 
19
21
  let showTooltip = false
20
22
 
21
23
  $: accentStyle = getAccentStyle(accentColor)
24
+ $: isCustomIconSource =
25
+ !!icon &&
26
+ (icon.includes("/") ||
27
+ icon.startsWith("http://") ||
28
+ icon.startsWith("https://") ||
29
+ icon.startsWith("data:"))
22
30
 
23
31
  const getAccentStyle = (color: string | null) => {
24
32
  if (!color) {
@@ -52,14 +60,25 @@
52
60
  {disabled}
53
61
  style={accentStyle}
54
62
  >
55
- {#if icon}
56
- <Icon
57
- name={icon}
58
- {size}
59
- color={`var(--spectrum-global-color-gray-${$$slots.default ? 600 : 700})`}
60
- />
63
+ {#if icon && !loading}
64
+ {#if isCustomIconSource}
65
+ <img
66
+ class="custom-icon custom-icon-{size}"
67
+ src={icon}
68
+ alt=""
69
+ aria-hidden="true"
70
+ />
71
+ {:else}
72
+ <Icon
73
+ name={icon}
74
+ {size}
75
+ color={`var(--spectrum-global-color-gray-${$$slots.default ? 600 : 700})`}
76
+ />
77
+ {/if}
61
78
  {/if}
62
- {#if $$slots}
79
+ {#if loading}
80
+ <ProgressCircle size="S" />
81
+ {:else if $$slots}
63
82
  <span class="spectrum-ActionButton-label"><slot /></span>
64
83
  {/if}
65
84
  {#if tooltip && showTooltip}
@@ -143,6 +162,22 @@
143
162
  .spectrum-ActionButton-label {
144
163
  font-weight: 600;
145
164
  }
165
+ .custom-icon {
166
+ object-fit: contain;
167
+ display: block;
168
+ }
169
+ .custom-icon-S {
170
+ width: 14px;
171
+ height: 14px;
172
+ }
173
+ .custom-icon-M {
174
+ width: 16px;
175
+ height: 16px;
176
+ }
177
+ .custom-icon-L {
178
+ width: 18px;
179
+ height: 18px;
180
+ }
146
181
  .tooltip {
147
182
  position: absolute;
148
183
  pointer-events: none;
@@ -49,7 +49,11 @@
49
49
  >
50
50
  <Menu>
51
51
  {#each buttons as button}
52
- <MenuItem on:click={() => handleClick(button)} disabled={button.disabled}>
52
+ <MenuItem
53
+ icon={button.icon}
54
+ on:click={() => handleClick(button)}
55
+ disabled={button.disabled}
56
+ >
53
57
  {button.text || "Button"}
54
58
  </MenuItem>
55
59
  {/each}
@@ -91,8 +91,8 @@
91
91
  signature.weight = 4
92
92
  signature.smoothing = 2
93
93
 
94
- canvasWrap.style.width = `${width}px`
95
- canvasWrap.style.height = `${height}px`
94
+ canvasWrap.style.width = "100%"
95
+ canvasWrap.style.maxWidth = `${width}px`
96
96
 
97
97
  const { width: wrapWidth, height: wrapHeight } =
98
98
  canvasWrap.getBoundingClientRect()
@@ -208,6 +208,7 @@
208
208
  }
209
209
  #signature-canvas {
210
210
  max-width: var(--max-sig-width);
211
+ width: 100%;
211
212
  max-height: var(--max-sig-height);
212
213
  }
213
214
  .signature.light img,
@@ -1,10 +1,10 @@
1
- <script lang="ts">
1
+ <script lang="ts" generics="V extends string | number">
2
2
  import "@spectrum-css/textfield/dist/index-vars.css"
3
3
  import { createEventDispatcher, onMount, tick } from "svelte"
4
4
  import type { FullAutoFill } from "svelte/elements"
5
5
  import type { UIEvent } from "@budibase/types"
6
6
 
7
- export let value: string | null = null
7
+ export let value: V | null = null
8
8
  export let placeholder: string | undefined = undefined
9
9
  export let type = "text"
10
10
  export let disabled = false
@@ -16,7 +16,7 @@
16
16
  export let autofocus: boolean | null = false
17
17
  export let autocomplete: FullAutoFill | boolean | null | undefined = undefined
18
18
 
19
- const dispatch = createEventDispatcher()
19
+ const dispatch = createEventDispatcher<{ change: V | null }>()
20
20
 
21
21
  let field: any
22
22
  let focus = false
@@ -1,10 +1,10 @@
1
- <script lang="ts">
1
+ <script lang="ts" generics="V extends string | number">
2
2
  import Field from "./Field.svelte"
3
3
  import TextField from "./Core/TextField.svelte"
4
4
  import { createEventDispatcher } from "svelte"
5
5
  import type { FullAutoFill } from "svelte/elements"
6
6
 
7
- export let value: any = undefined
7
+ export let value: V | null | undefined = undefined
8
8
  export let label: string | undefined = undefined
9
9
  export let labelPosition: "above" | "left" = "above"
10
10
  export let placeholder: string | undefined = undefined
@@ -21,7 +21,7 @@
21
21
  export let description: string | undefined = undefined
22
22
 
23
23
  const dispatch = createEventDispatcher<{
24
- change: any
24
+ change: V
25
25
  enterkey: KeyboardEvent
26
26
  blur: any
27
27
  }>()
@@ -1,4 +1,7 @@
1
- <script lang="ts" generics="O extends any,V">
1
+ <script
2
+ lang="ts"
3
+ generics="O extends any, V, P extends string | boolean = string | boolean"
4
+ >
2
5
  import Field from "./Field.svelte"
3
6
  import Select from "./Core/Select.svelte"
4
7
  import { createEventDispatcher } from "svelte"
@@ -11,7 +14,7 @@
11
14
  export let readonly: boolean = false
12
15
  export let labelPosition: LabelPosition = "above"
13
16
  export let error: string | undefined = undefined
14
- export let placeholder: string | boolean = "Choose an option"
17
+ export let placeholder: P = "Choose an option" as P
15
18
  export let options: O[] = []
16
19
  export let getOptionLabel = (option: O, _index?: number) =>
17
20
  extractProperty(option, "label")
@@ -53,8 +56,10 @@
53
56
  export let wrapText: boolean = false
54
57
  export let description: string | undefined = undefined
55
58
 
56
- const dispatch = createEventDispatcher()
57
- const onChange = (e: CustomEvent<any>) => {
59
+ type ChangeValue = P extends false | "" ? V : V | undefined
60
+
61
+ const dispatch = createEventDispatcher<{ change: ChangeValue }>()
62
+ const onChange = (e: CustomEvent<ChangeValue>) => {
58
63
  value = e.detail
59
64
  dispatch("change", e.detail)
60
65
  }
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import type EasyMDE from "easymde"
3
+ import { onDestroy } from "svelte"
2
4
  import SpectrumMDE from "./SpectrumMDE.svelte"
3
5
  import { createEventDispatcher } from "svelte"
4
6
 
@@ -14,26 +16,53 @@
14
16
  const dispatch = createEventDispatcher()
15
17
 
16
18
  let latestValue: string | null
17
- let mde: any
19
+ interface EditorInstance extends EasyMDE {
20
+ togglePreview: () => void
21
+ value: (_value?: string) => string
22
+ }
23
+ let mde: EditorInstance | null = null
24
+ let blurBoundTo: EditorInstance | null = null
25
+
26
+ const bindBlurHandler = (editor: EditorInstance) => {
27
+ if (blurBoundTo === editor) {
28
+ return
29
+ }
30
+ if (blurBoundTo) {
31
+ blurBoundTo.codemirror.off("blur", update)
32
+ }
33
+ editor.codemirror.on("blur", update)
34
+ blurBoundTo = editor
35
+ }
18
36
 
19
37
  // Ensure the value is updated if the value prop changes outside the editor's
20
38
  // control
21
- $: checkValue(value)
22
- $: mde?.codemirror.on("blur", update)
23
- $: if (readonly || disabled) {
24
- mde?.togglePreview()
39
+ $: checkValue(mde, value)
40
+ $: if (mde) {
41
+ bindBlurHandler(mde)
42
+ }
43
+ $: if ((readonly || disabled) && mde) {
44
+ mde.togglePreview?.()
25
45
  }
26
46
 
27
- const checkValue = (val: string | null) => {
28
- if (mde && val !== latestValue) {
29
- mde.value(val)
47
+ const checkValue = (editor: EditorInstance | null, val: string | null) => {
48
+ if (editor && val !== latestValue) {
49
+ editor.value(val ?? "")
30
50
  }
31
51
  }
32
52
 
33
53
  const update = () => {
54
+ if (!mde) {
55
+ return
56
+ }
34
57
  latestValue = mde.value()
35
58
  dispatch("change", latestValue)
36
59
  }
60
+
61
+ onDestroy(() => {
62
+ if (blurBoundTo) {
63
+ blurBoundTo.codemirror.off("blur", update)
64
+ }
65
+ })
37
66
  </script>
38
67
 
39
68
  {#key height}
@@ -47,8 +76,8 @@
47
76
  easyMDEOptions={{
48
77
  initialValue: value,
49
78
  placeholder,
50
- toolbar: disabled || readonly ? false : undefined,
51
79
  ...easyMDEOptions,
80
+ toolbar: disabled || readonly ? false : easyMDEOptions?.toolbar,
52
81
  }}
53
82
  />
54
83
  {/key}
@@ -1,17 +1,322 @@
1
1
  <script lang="ts">
2
2
  import type EasyMDE from "easymde"
3
3
  import "easymde/dist/easymde.min.css"
4
- import { onDestroy, onMount } from "svelte"
4
+ import { onDestroy, onMount, tick } from "svelte"
5
+ import ColorPicker from "../ColorPicker/ColorPicker.svelte"
5
6
 
6
7
  export let height: string | null = null
7
8
  export let scroll: boolean = true
8
9
  export let easyMDEOptions: Record<string, any> | null = null
9
- export let mde: EasyMDE | null = null
10
+ interface EditorInstance extends EasyMDE {
11
+ toolbarElements?: Record<string, HTMLElement>
12
+ }
13
+ interface EditorSelectionRange {
14
+ anchor: { line: number; ch: number }
15
+ head: { line: number; ch: number }
16
+ }
17
+ type ColorMode = "text" | "highlight"
18
+ type ActiveColors = Record<ColorMode, string | null>
19
+
20
+ export let mde: EditorInstance | null = null
10
21
  export let id: string | null = null
11
22
  export let fullScreenOffset: { x: string; y: string } | null = null
12
23
  export let disabled: boolean = false
13
24
 
14
25
  let element: HTMLTextAreaElement | undefined = undefined
26
+ let colorPickerAnchor: HTMLDivElement | undefined = undefined
27
+ let colorPickerX = 0
28
+ let colorPickerY = 0
29
+ let colorPickerKey = 0
30
+ let activeMode: ColorMode = "text"
31
+ let selectedColors: Record<ColorMode, string> = {
32
+ text: "#d73f09",
33
+ highlight: "#fff59d",
34
+ }
35
+ let pendingSelections: EditorSelectionRange[] | null = null
36
+ let lastNonEmptySelections: EditorSelectionRange[] | null = null
37
+ let cursorBoundTo: EditorInstance | null = null
38
+
39
+ const modeConfig = {
40
+ text: {
41
+ toolbarButtonName: "text-color",
42
+ wrapperTag: "span",
43
+ stylePrefix: "color",
44
+ className: "fa fa-font",
45
+ title: "Text Color",
46
+ },
47
+ highlight: {
48
+ toolbarButtonName: "text-highlight",
49
+ wrapperTag: "mark",
50
+ stylePrefix: "background-color",
51
+ className: "fa fa-paint-brush",
52
+ title: "Highlight Color",
53
+ },
54
+ } as const
55
+
56
+ const cloneSelections = (selections: EditorSelectionRange[]) =>
57
+ selections.map(selection => ({
58
+ anchor: { ...selection.anchor },
59
+ head: { ...selection.head },
60
+ }))
61
+
62
+ const hasSelectedText = (editor: EditorInstance) =>
63
+ editor.codemirror.getSelections().some((text: string) => text.length > 0)
64
+
65
+ const getSelections = (editor: EditorInstance) =>
66
+ cloneSelections(
67
+ editor.codemirror.listSelections() as EditorSelectionRange[]
68
+ )
69
+
70
+ const getValidatedColor = (color: string, mode: ColorMode) => {
71
+ const trimmedColor = color.trim()
72
+ if (!trimmedColor) {
73
+ return null
74
+ }
75
+
76
+ const cssProperty = modeConfig[mode].stylePrefix
77
+ if (typeof CSS === "undefined" || typeof CSS.supports !== "function") {
78
+ return null
79
+ }
80
+ if (!CSS.supports(cssProperty, trimmedColor)) {
81
+ return null
82
+ }
83
+ return trimmedColor
84
+ }
85
+
86
+ const getColorFromTag = (tagText: string, mode: ColorMode) => {
87
+ const styleRegex = new RegExp(
88
+ `${modeConfig[mode].stylePrefix}\\s*:\\s*([^;"']+)`,
89
+ "i"
90
+ )
91
+ const styleMatch = tagText.match(styleRegex)
92
+ if (!styleMatch?.[1]) {
93
+ return null
94
+ }
95
+ return getValidatedColor(styleMatch[1], mode)
96
+ }
97
+
98
+ const getActiveColorsBeforeCursor = (
99
+ textBeforeCursor: string
100
+ ): ActiveColors => {
101
+ const stacks: Record<ColorMode, string[]> = {
102
+ text: [],
103
+ highlight: [],
104
+ }
105
+ const tokenRegex = /<(\/?)(span|mark)\b[^>]*>/gi
106
+ for (const token of textBeforeCursor.matchAll(tokenRegex)) {
107
+ const tokenValue = token[0]
108
+ const isClosing = token[1] === "/"
109
+ const mode: ColorMode =
110
+ token[2].toLowerCase() === "mark" ? "highlight" : "text"
111
+ if (isClosing) {
112
+ stacks[mode].pop()
113
+ continue
114
+ }
115
+ const color = getColorFromTag(tokenValue, mode)
116
+ if (color) {
117
+ stacks[mode].push(color)
118
+ }
119
+ }
120
+ return {
121
+ text: stacks.text.length ? stacks.text[stacks.text.length - 1] : null,
122
+ highlight: stacks.highlight.length
123
+ ? stacks.highlight[stacks.highlight.length - 1]
124
+ : null,
125
+ }
126
+ }
127
+
128
+ const getColorsAtCursor = (
129
+ content: string,
130
+ cursorIndex: number
131
+ ): ActiveColors => {
132
+ const result: ActiveColors = {
133
+ text: null,
134
+ highlight: null,
135
+ }
136
+ const openIndex = content.lastIndexOf("<", cursorIndex)
137
+ if (openIndex < 0) {
138
+ return result
139
+ }
140
+ const closeBeforeIndex = content.lastIndexOf(">", cursorIndex - 1)
141
+ if (closeBeforeIndex > openIndex) {
142
+ return result
143
+ }
144
+ const closeAfterIndex = content.indexOf(">", openIndex)
145
+ if (closeAfterIndex < 0) {
146
+ return result
147
+ }
148
+ const tagText = content.slice(openIndex, closeAfterIndex + 1)
149
+ if (/^<span\b/i.test(tagText)) {
150
+ result.text = getColorFromTag(tagText, "text")
151
+ }
152
+ if (/^<mark\b/i.test(tagText)) {
153
+ result.highlight = getColorFromTag(tagText, "highlight")
154
+ }
155
+ return result
156
+ }
157
+
158
+ const setToolbarIconColor = (
159
+ editor: EditorInstance,
160
+ mode: ColorMode,
161
+ color: string | null
162
+ ) => {
163
+ const button = editor.toolbarElements?.[modeConfig[mode].toolbarButtonName]
164
+ const icon = button?.querySelector("i") as HTMLElement | null
165
+ if (!icon) {
166
+ return
167
+ }
168
+ if (!color) {
169
+ icon.style.removeProperty("color")
170
+ return
171
+ }
172
+ icon.style.color = color
173
+ }
174
+
175
+ const syncToolbarIconColors = (editor: EditorInstance) => {
176
+ const cursor = editor.codemirror.getCursor("head")
177
+ const cursorIndex = editor.codemirror.indexFromPos(cursor)
178
+ const content = editor.codemirror.getValue()
179
+ const contentBeforeCursor = content.slice(0, cursorIndex)
180
+ const activeColors = getActiveColorsBeforeCursor(contentBeforeCursor)
181
+ const cursorColors = getColorsAtCursor(content, cursorIndex)
182
+ const textColor = cursorColors.text || activeColors.text
183
+ const highlightColor = cursorColors.highlight || activeColors.highlight
184
+ setToolbarIconColor(editor, "text", textColor)
185
+ setToolbarIconColor(editor, "highlight", highlightColor)
186
+ }
187
+
188
+ const cacheSelection = () => {
189
+ if (!mde || !hasSelectedText(mde)) {
190
+ return
191
+ }
192
+ lastNonEmptySelections = getSelections(mde)
193
+ }
194
+
195
+ const onCursorActivity = () => {
196
+ if (!mde) {
197
+ return
198
+ }
199
+ cacheSelection()
200
+ syncToolbarIconColors(mde)
201
+ }
202
+
203
+ const bindCursorListener = (editor: EditorInstance) => {
204
+ if (cursorBoundTo === editor) {
205
+ return
206
+ }
207
+ if (cursorBoundTo) {
208
+ cursorBoundTo.codemirror.off("cursorActivity", onCursorActivity)
209
+ }
210
+ editor.codemirror.on("cursorActivity", onCursorActivity)
211
+ cursorBoundTo = editor
212
+ }
213
+
214
+ const openColorPicker = async (editor: EasyMDE, mode: ColorMode) => {
215
+ const typedEditor = editor as EditorInstance
216
+ const currentSelections = getSelections(typedEditor)
217
+ const activeSelections = hasSelectedText(typedEditor)
218
+ ? currentSelections
219
+ : lastNonEmptySelections || currentSelections
220
+ pendingSelections = cloneSelections(activeSelections)
221
+ activeMode = mode
222
+ const toolbarButtonName = modeConfig[mode].toolbarButtonName
223
+ const toolbarButton = typedEditor.toolbarElements?.[toolbarButtonName]
224
+ if (toolbarButton) {
225
+ const rect = toolbarButton.getBoundingClientRect()
226
+ colorPickerX = Math.round(rect.left + rect.width / 2)
227
+ colorPickerY = Math.round(rect.bottom)
228
+ }
229
+ colorPickerKey += 1
230
+ await tick()
231
+ const trigger = colorPickerAnchor?.querySelector(".preview") as
232
+ | HTMLElement
233
+ | undefined
234
+ trigger?.click()
235
+ }
236
+
237
+ const applyStyledSelections = (color: string, mode: ColorMode) => {
238
+ if (!pendingSelections || !mde) {
239
+ return
240
+ }
241
+ const safeColor = getValidatedColor(color, mode)
242
+ if (!safeColor) {
243
+ return
244
+ }
245
+ mde.codemirror.focus()
246
+ mde.codemirror.setSelections(pendingSelections)
247
+ const selectedTexts = mde.codemirror.getSelections()
248
+ const hasText = selectedTexts?.some((text: string) => text.length > 0)
249
+ if (!selectedTexts?.length || !hasText) {
250
+ pendingSelections = null
251
+ return
252
+ }
253
+ const { wrapperTag, stylePrefix } = modeConfig[mode]
254
+ const styleAttr = `${stylePrefix}: ${safeColor};`
255
+ const replacements = selectedTexts.map(
256
+ (text: string) =>
257
+ `<${wrapperTag} style="${styleAttr}">${text}</${wrapperTag}>`
258
+ )
259
+ mde.codemirror.replaceSelections(replacements)
260
+ pendingSelections = null
261
+ }
262
+
263
+ const onColorChange = (event: CustomEvent<string | undefined>) => {
264
+ const color = event.detail?.trim()
265
+ if (!color) {
266
+ return
267
+ }
268
+ selectedColors = {
269
+ ...selectedColors,
270
+ [activeMode]: color,
271
+ }
272
+ applyStyledSelections(color, activeMode)
273
+ if (mde) {
274
+ syncToolbarIconColors(mde)
275
+ }
276
+ }
277
+
278
+ const textColorToolbarButton = {
279
+ name: modeConfig.text.toolbarButtonName,
280
+ action: (editor: EasyMDE) => openColorPicker(editor, "text"),
281
+ className: modeConfig.text.className,
282
+ title: modeConfig.text.title,
283
+ }
284
+
285
+ const highlightToolbarButton = {
286
+ name: modeConfig.highlight.toolbarButtonName,
287
+ action: (editor: EasyMDE) => openColorPicker(editor, "highlight"),
288
+ className: modeConfig.highlight.className,
289
+ title: modeConfig.highlight.title,
290
+ }
291
+
292
+ const defaultToolbar = [
293
+ "bold",
294
+ "italic",
295
+ "heading",
296
+ "|",
297
+ "quote",
298
+ "unordered-list",
299
+ "ordered-list",
300
+ "|",
301
+ "link",
302
+ "image",
303
+ "|",
304
+ textColorToolbarButton,
305
+ highlightToolbarButton,
306
+ "|",
307
+ "preview",
308
+ "side-by-side",
309
+ "fullscreen",
310
+ "|",
311
+ "guide",
312
+ ]
313
+
314
+ const getToolbar = (options: Record<string, any> | null) => {
315
+ if (options?.toolbar !== undefined) {
316
+ return options.toolbar
317
+ }
318
+ return defaultToolbar
319
+ }
15
320
 
16
321
  onMount(async () => {
17
322
  height = height || "200px"
@@ -24,10 +329,16 @@
24
329
  maxHeight: scroll ? height : undefined,
25
330
  minHeight: scroll ? undefined : height,
26
331
  ...easyMDEOptions,
332
+ toolbar: getToolbar(easyMDEOptions),
27
333
  })
334
+ bindCursorListener(mde)
335
+ syncToolbarIconColors(mde)
28
336
  })
29
337
 
30
338
  onDestroy(() => {
339
+ if (cursorBoundTo) {
340
+ cursorBoundTo.codemirror.off("cursorActivity", onCursorActivity)
341
+ }
31
342
  mde?.toTextArea()
32
343
  })
33
344
 
@@ -45,7 +356,30 @@
45
356
  <textarea disabled {id} bind:this={element}></textarea>
46
357
  </div>
47
358
 
359
+ <div
360
+ bind:this={colorPickerAnchor}
361
+ class="budibase-color-picker-anchor"
362
+ style={`left:${colorPickerX}px;top:${colorPickerY}px;`}
363
+ >
364
+ {#key colorPickerKey}
365
+ <ColorPicker
366
+ value={selectedColors[activeMode]}
367
+ size="S"
368
+ on:change={onColorChange}
369
+ />
370
+ {/key}
371
+ </div>
372
+
48
373
  <style>
374
+ .budibase-color-picker-anchor {
375
+ position: fixed;
376
+ width: 1px;
377
+ height: 1px;
378
+ overflow: hidden;
379
+ opacity: 0;
380
+ pointer-events: none;
381
+ }
382
+
49
383
  /* Disabled styles */
50
384
  .disabled :global(textarea) {
51
385
  display: none;
@@ -114,6 +448,17 @@
114
448
  :global(.EasyMDEContainer .cm-s-easymde .cm-url) {
115
449
  color: var(--spectrum-global-color-gray-500);
116
450
  }
451
+ /* Keep inline HTML markup readable across themes */
452
+ :global(.EasyMDEContainer .cm-s-easymde .cm-tag) {
453
+ color: var(--spectrum-alias-text-color);
454
+ opacity: 0.9;
455
+ }
456
+ :global(.EasyMDEContainer .cm-s-easymde .cm-attribute) {
457
+ color: var(--spectrum-global-color-gray-700);
458
+ }
459
+ :global(.EasyMDEContainer .cm-s-easymde .cm-string) {
460
+ color: var(--spectrum-global-color-gray-600);
461
+ }
117
462
  /* Markdown italics in the editor */
118
463
  :global(.EasyMDEContainer .cm-s-easymde .cm-em) {
119
464
  font-style: italic !important;