@m3ui-vue/m3ui-vue 0.1.1 → 0.1.2

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 (57) hide show
  1. package/README.md +1 -3
  2. package/dist/MIcon-CaEooCmZ.js +20 -0
  3. package/dist/MIcon-CaEooCmZ.js.map +1 -0
  4. package/dist/_plugin-vue_export-helper-B3ysoDQm.js +8 -0
  5. package/dist/chart.d.ts +1 -0
  6. package/dist/chart.js +141 -0
  7. package/dist/chart.js.map +1 -0
  8. package/dist/code-editor.d.ts +2 -0
  9. package/dist/code-editor.js +379 -0
  10. package/dist/code-editor.js.map +1 -0
  11. package/dist/components/MButton.vue.d.ts +1 -1
  12. package/dist/components/MCalendar.vue.d.ts +1 -1
  13. package/dist/components/MCodeEditor.vue.d.ts +3 -1
  14. package/dist/components/MDataTable.vue.d.ts +1 -1
  15. package/dist/components/MFab.vue.d.ts +0 -2
  16. package/dist/components/MIconButton.vue.d.ts +1 -1
  17. package/dist/components/MMultiSelect.vue.d.ts +1 -1
  18. package/dist/components/MProgressBar.vue.d.ts +1 -1
  19. package/dist/components/MRichTextEditor.vue.d.ts +1 -1
  20. package/dist/components/MScheduler.vue.d.ts +1 -1
  21. package/dist/components/MSelect.vue.d.ts +1 -1
  22. package/dist/components/MSkeleton.vue.d.ts +1 -1
  23. package/dist/components/MSpotlightSearch.vue.d.ts +1 -1
  24. package/dist/components/MStack.vue.d.ts +2 -2
  25. package/dist/components/MTerminal.vue.d.ts +2 -2
  26. package/dist/components/MTextField.vue.d.ts +1 -1
  27. package/dist/dist-Dsrzt6J5.js +1192 -0
  28. package/dist/dist-Dsrzt6J5.js.map +1 -0
  29. package/dist/index.d.ts +0 -6
  30. package/dist/m3ui-vue.css +2 -0
  31. package/dist/m3ui.js +2722 -3383
  32. package/dist/m3ui.js.map +1 -1
  33. package/dist/markdown.d.ts +1 -0
  34. package/dist/markdown.js +41 -0
  35. package/dist/markdown.js.map +1 -0
  36. package/dist/rich-text-editor.d.ts +1 -0
  37. package/dist/rich-text-editor.js +215 -0
  38. package/dist/rich-text-editor.js.map +1 -0
  39. package/dist/styles/theme.css +3 -0
  40. package/dist/styles.css +2 -0
  41. package/dist/terminal.d.ts +1 -0
  42. package/dist/terminal.js +97 -0
  43. package/dist/terminal.js.map +1 -0
  44. package/package.json +28 -2
  45. package/src/chart.ts +1 -0
  46. package/src/code-editor.ts +2 -0
  47. package/src/components/MAlert.vue +1 -1
  48. package/src/components/MCodeEditor.vue +99 -10
  49. package/src/components/MFab.vue +64 -48
  50. package/src/components/MMultiSelect.vue +3 -2
  51. package/src/components/MTooltip.vue +8 -1
  52. package/src/index.ts +6 -6
  53. package/src/markdown.ts +1 -0
  54. package/src/rich-text-editor.ts +1 -0
  55. package/src/styles/theme.css +3 -0
  56. package/src/terminal.ts +1 -0
  57. package/dist/m3ui.css +0 -2
@@ -1,7 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, watch, onMounted, onBeforeUnmount, computed } from 'vue'
3
+ import MIcon from './MIcon.vue'
3
4
 
4
- type Language = 'javascript' | 'typescript' | 'json' | 'html' | 'css' | 'python' | 'plain'
5
+ type Language = 'javascript' | 'typescript' | 'json' | 'html' | 'css' | 'python' | 'vue' | 'plain'
5
6
 
6
7
  const props = withDefaults(
7
8
  defineProps<{
@@ -13,6 +14,7 @@ const props = withDefaults(
13
14
  minHeight?: string
14
15
  maxHeight?: string
15
16
  placeholder?: string
17
+ wrap?: boolean
16
18
  }>(),
17
19
  {
18
20
  language: 'javascript',
@@ -21,6 +23,7 @@ const props = withDefaults(
21
23
  theme: 'light',
22
24
  minHeight: '200px',
23
25
  maxHeight: '600px',
26
+ wrap: true,
24
27
  },
25
28
  )
26
29
 
@@ -29,6 +32,13 @@ const emit = defineEmits<{ 'update:modelValue': [string] }>()
29
32
  const containerRef = ref<HTMLElement | null>(null)
30
33
  let view: any = null
31
34
  let cmModules: any = null
35
+ const copied = ref(false)
36
+
37
+ async function copyCode() {
38
+ await navigator.clipboard.writeText(props.modelValue)
39
+ copied.value = true
40
+ setTimeout(() => { copied.value = false }, 1500)
41
+ }
32
42
 
33
43
  const langLabel = computed(() => {
34
44
  const labels: Record<Language, string> = {
@@ -38,6 +48,7 @@ const langLabel = computed(() => {
38
48
  html: 'HTML',
39
49
  css: 'CSS',
40
50
  python: 'Python',
51
+ vue: 'Vue',
41
52
  plain: 'Texto',
42
53
  }
43
54
  return labels[props.language]
@@ -46,23 +57,71 @@ const langLabel = computed(() => {
46
57
  async function loadModules() {
47
58
  if (cmModules) return cmModules
48
59
 
49
- const [viewMod, stateMod, commandsMod, languageMod, oneDarkMod, jsMod, jsonMod, htmlMod, cssMod, pyMod] = await Promise.all([
60
+ const [viewMod, stateMod, commandsMod, languageMod, highlightMod, oneDarkMod, jsMod, jsonMod, htmlMod, cssMod, pyMod, vueMod] = await Promise.all([
50
61
  import('@codemirror/view'),
51
62
  import('@codemirror/state'),
52
63
  import('@codemirror/commands'),
53
64
  import('@codemirror/language'),
65
+ import('@lezer/highlight'),
54
66
  import('@codemirror/theme-one-dark'),
55
67
  import('@codemirror/lang-javascript'),
56
68
  import('@codemirror/lang-json'),
57
69
  import('@codemirror/lang-html'),
58
70
  import('@codemirror/lang-css'),
59
71
  import('@codemirror/lang-python'),
72
+ import('@codemirror/lang-vue'),
60
73
  ])
61
74
 
62
- cmModules = { viewMod, stateMod, commandsMod, languageMod, oneDarkMod, jsMod, jsonMod, htmlMod, cssMod, pyMod }
75
+ cmModules = { viewMod, stateMod, commandsMod, languageMod, highlightMod, oneDarkMod, jsMod, jsonMod, htmlMod, cssMod, pyMod, vueMod }
63
76
  return cmModules
64
77
  }
65
78
 
79
+ function buildM3HighlightStyle(languageMod: any, tags: any) {
80
+ return languageMod.HighlightStyle.define([
81
+ { tag: tags.keyword, color: 'var(--color-primary)' },
82
+ { tag: tags.controlKeyword, color: 'var(--color-primary)', fontWeight: '500' },
83
+ { tag: tags.operatorKeyword, color: 'var(--color-primary)' },
84
+ { tag: tags.definitionKeyword, color: 'var(--color-primary)' },
85
+ { tag: tags.moduleKeyword, color: 'var(--color-primary)' },
86
+
87
+ { tag: tags.string, color: 'var(--color-tertiary)' },
88
+ { tag: tags.regexp, color: 'var(--color-tertiary)' },
89
+
90
+ { tag: tags.number, color: 'var(--color-error)' },
91
+ { tag: tags.bool, color: 'var(--color-error)' },
92
+
93
+ { tag: tags.function(tags.variableName), color: 'var(--color-secondary)' },
94
+ { tag: tags.function(tags.definition(tags.variableName)), color: 'var(--color-secondary)', fontWeight: '500' },
95
+
96
+ { tag: tags.typeName, color: 'var(--color-primary)', fontStyle: 'italic' },
97
+ { tag: tags.className, color: 'var(--color-primary)', fontStyle: 'italic' },
98
+ { tag: tags.namespace, color: 'var(--color-on-surface-variant)' },
99
+
100
+ { tag: tags.propertyName, color: 'var(--color-on-surface)' },
101
+ { tag: tags.definition(tags.propertyName), color: 'var(--color-on-surface)' },
102
+
103
+ { tag: tags.variableName, color: 'var(--color-on-surface)' },
104
+ { tag: tags.definition(tags.variableName), color: 'var(--color-on-surface)' },
105
+
106
+ { tag: tags.comment, color: 'var(--color-outline)', fontStyle: 'italic' },
107
+ { tag: tags.lineComment, color: 'var(--color-outline)', fontStyle: 'italic' },
108
+ { tag: tags.blockComment, color: 'var(--color-outline)', fontStyle: 'italic' },
109
+
110
+ { tag: tags.meta, color: 'var(--color-on-surface-variant)' },
111
+ { tag: tags.tagName, color: 'var(--color-primary)' },
112
+ { tag: tags.attributeName, color: 'var(--color-tertiary)' },
113
+ { tag: tags.attributeValue, color: 'var(--color-secondary)' },
114
+
115
+ { tag: tags.atom, color: 'var(--color-error)' },
116
+ { tag: tags.null, color: 'var(--color-error)' },
117
+
118
+ { tag: tags.punctuation, color: 'var(--color-on-surface-variant)' },
119
+ { tag: tags.bracket, color: 'var(--color-on-surface-variant)' },
120
+ { tag: tags.operator, color: 'var(--color-on-surface-variant)' },
121
+ { tag: tags.separator, color: 'var(--color-on-surface-variant)' },
122
+ ])
123
+ }
124
+
66
125
  function getLangExtension(mods: any) {
67
126
  switch (props.language) {
68
127
  case 'javascript': return mods.jsMod.javascript()
@@ -71,12 +130,15 @@ function getLangExtension(mods: any) {
71
130
  case 'html': return mods.htmlMod.html()
72
131
  case 'css': return mods.cssMod.css()
73
132
  case 'python': return mods.pyMod.python()
133
+ case 'vue': return mods.vueMod.vue()
74
134
  default: return []
75
135
  }
76
136
  }
77
137
 
78
138
  function buildExtensions(mods: any) {
79
- const { viewMod, stateMod, commandsMod, languageMod, oneDarkMod } = mods
139
+ const { viewMod, stateMod, commandsMod, languageMod, highlightMod, oneDarkMod } = mods
140
+
141
+ const m3Style = buildM3HighlightStyle(languageMod, highlightMod.tags)
80
142
 
81
143
  const exts = [
82
144
  viewMod.keymap.of([...commandsMod.defaultKeymap, ...commandsMod.historyKeymap, commandsMod.indentWithTab]),
@@ -86,6 +148,7 @@ function buildExtensions(mods: any) {
86
148
  languageMod.foldGutter(),
87
149
  viewMod.highlightActiveLine(),
88
150
  viewMod.highlightActiveLineGutter(),
151
+ languageMod.syntaxHighlighting(m3Style),
89
152
  languageMod.syntaxHighlighting(languageMod.defaultHighlightStyle, { fallback: true }),
90
153
  getLangExtension(mods),
91
154
  viewMod.EditorView.updateListener.of((update: any) => {
@@ -94,6 +157,7 @@ function buildExtensions(mods: any) {
94
157
  stateMod.EditorState.readOnly.of(props.readonly),
95
158
  ]
96
159
 
160
+ if (props.wrap) exts.push(viewMod.EditorView.lineWrapping)
97
161
  if (props.lineNumbers) exts.push(viewMod.lineNumbers())
98
162
  if (props.theme === 'dark') exts.push(oneDarkMod.oneDark)
99
163
 
@@ -124,7 +188,7 @@ watch(() => props.modelValue, (val) => {
124
188
  }
125
189
  })
126
190
 
127
- watch([() => props.language, () => props.theme, () => props.readonly, () => props.lineNumbers], createEditor)
191
+ watch([() => props.language, () => props.theme, () => props.readonly, () => props.lineNumbers, () => props.wrap], createEditor)
128
192
 
129
193
  onBeforeUnmount(() => view?.destroy())
130
194
  </script>
@@ -134,13 +198,24 @@ onBeforeUnmount(() => view?.destroy())
134
198
  <!-- Header bar -->
135
199
  <div class="flex items-center justify-between border-b border-outline-variant bg-surface-container px-4 py-2">
136
200
  <span class="text-label-medium text-on-surface-variant">{{ langLabel }}</span>
137
- <slot name="actions" />
201
+ <div class="flex items-center gap-2">
202
+ <slot name="actions" />
203
+ <button
204
+ type="button"
205
+ class="flex h-7 cursor-pointer items-center gap-1.5 rounded-md px-2 text-label-medium text-on-surface-variant transition-colors hover:bg-on-surface/8"
206
+ :title="copied ? 'Copied!' : 'Copy code'"
207
+ @click="copyCode"
208
+ >
209
+ <MIcon :name="copied ? 'check' : 'content_copy'" :size="16" :class="copied ? 'text-primary' : ''" />
210
+ <span v-if="copied" class="text-primary">Copied</span>
211
+ </button>
212
+ </div>
138
213
  </div>
139
214
 
140
215
  <!-- Editor -->
141
216
  <div
142
217
  ref="containerRef"
143
- class="code-editor-container overflow-auto bg-surface"
218
+ class="code-editor-container overflow-auto bg-surface text-on-surface"
144
219
  :style="{ minHeight, maxHeight }"
145
220
  />
146
221
  </div>
@@ -150,8 +225,9 @@ onBeforeUnmount(() => view?.destroy())
150
225
  .code-editor-container :deep(.cm-editor) {
151
226
  height: 100%;
152
227
  min-height: inherit;
153
- font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
154
- font-size: 0.875rem;
228
+ font-family: 'Roboto Mono', 'Fira Code', 'Consolas', monospace;
229
+ font-size: 0.8125rem;
230
+ line-height: 1.6;
155
231
  }
156
232
 
157
233
  .code-editor-container :deep(.cm-editor.cm-focused) {
@@ -162,14 +238,25 @@ onBeforeUnmount(() => view?.destroy())
162
238
  min-height: inherit;
163
239
  }
164
240
 
241
+ .code-editor-container :deep(.cm-content) {
242
+ padding: 12px 0;
243
+ }
244
+
245
+ .code-editor-container :deep(.cm-line) {
246
+ padding: 0 16px;
247
+ }
248
+
165
249
  .code-editor-container :deep(.cm-gutters) {
166
250
  background: var(--color-surface-container);
167
251
  border-right: 1px solid var(--color-outline-variant);
168
- color: var(--color-on-surface-variant);
252
+ color: var(--color-outline);
253
+ font-size: 0.75rem;
254
+ padding: 0 4px;
169
255
  }
170
256
 
171
257
  .code-editor-container :deep(.cm-activeLineGutter) {
172
258
  background: var(--color-surface-container-high);
259
+ color: var(--color-on-surface-variant);
173
260
  }
174
261
 
175
262
  .code-editor-container :deep(.cm-activeLine) {
@@ -182,11 +269,13 @@ onBeforeUnmount(() => view?.destroy())
182
269
 
183
270
  .code-editor-container :deep(.cm-cursor) {
184
271
  border-left-color: var(--color-primary);
272
+ border-left-width: 2px;
185
273
  }
186
274
 
187
275
  .code-editor-container :deep(.cm-matchingBracket) {
188
276
  background: var(--color-tertiary-container);
189
277
  color: var(--color-on-tertiary-container);
278
+ border-radius: 2px;
190
279
  }
191
280
 
192
281
  .code-editor-container :deep(.cm-foldGutter span) {
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, onMounted, onUnmounted, ref } from 'vue'
2
+ import { computed, onMounted, onUnmounted, ref, watch, nextTick } from 'vue'
3
3
  import MIcon from './MIcon.vue'
4
4
 
5
5
  export interface SpeedDialItem {
@@ -15,9 +15,7 @@ const props = withDefaults(
15
15
  color?: 'primary' | 'secondary' | 'tertiary' | 'surface'
16
16
  size?: 'small' | 'regular' | 'large'
17
17
  disabled?: boolean
18
- /** Speed-dial child items. If provided, clicking the FAB toggles them instead of emitting click. */
19
18
  items?: SpeedDialItem[]
20
- /** Direction the speed-dial items expand toward. */
21
19
  direction?: 'up' | 'down' | 'left' | 'right' | 'radial'
22
20
  }>(),
23
21
  {
@@ -31,7 +29,7 @@ const props = withDefaults(
31
29
  const emit = defineEmits<{ click: [MouseEvent] }>()
32
30
 
33
31
  const open = ref(false)
34
- const containerEl = ref<HTMLElement>()
32
+ const fabEl = ref<HTMLElement>()
35
33
 
36
34
  const hasItems = computed(() => !!props.items?.length)
37
35
 
@@ -60,7 +58,6 @@ const fabIconSize = computed(() => {
60
58
  }
61
59
  })
62
60
 
63
- // FAB height in px — used to position items relative to the container
64
61
  const fabPx = computed(() => {
65
62
  if (props.label) return 56
66
63
  switch (props.size) {
@@ -70,17 +67,24 @@ const fabPx = computed(() => {
70
67
  }
71
68
  })
72
69
 
73
- // Item size (always small-FAB-sized): 40px
74
70
  const ITEM_PX = 40
75
71
  const ITEM_GAP = 8
76
72
 
73
+ function getRect(): DOMRect | null {
74
+ return fabEl.value?.getBoundingClientRect() ?? null
75
+ }
76
+
77
77
  function itemStyle(index: number): Record<string, string> {
78
+ const rect = getRect()
79
+ if (!rect) return { position: 'fixed', opacity: '0', pointerEvents: 'none' }
80
+
81
+ const cx = rect.left + rect.width / 2
82
+ const cy = rect.top + rect.height / 2
78
83
  const count = props.items?.length ?? 0
79
- // Stagger delay: open = forward order, close = reverse order
84
+
80
85
  const delay = open.value
81
86
  ? `${index * 35}ms`
82
87
  : `${(count - 1 - index) * 35}ms`
83
-
84
88
  const transition = `transform 220ms cubic-bezier(0.2,0,0,1) ${delay}, opacity 180ms ease ${delay}`
85
89
 
86
90
  if (props.direction === 'radial') {
@@ -89,27 +93,25 @@ function itemStyle(index: number): Record<string, string> {
89
93
  const dx = (Math.cos(angle) * r).toFixed(1)
90
94
  const dy = (Math.sin(angle) * r).toFixed(1)
91
95
  return {
92
- position: 'absolute',
93
- top: '50%',
94
- left: '50%',
95
- marginTop: `${-ITEM_PX / 2}px`,
96
- marginLeft: `${-ITEM_PX / 2}px`,
96
+ position: 'fixed',
97
+ top: `${cy - ITEM_PX / 2}px`,
98
+ left: `${cx - ITEM_PX / 2}px`,
97
99
  transform: open.value ? `translate(${dx}px, ${dy}px) scale(1)` : 'translate(0,0) scale(0)',
98
100
  opacity: open.value ? '1' : '0',
99
101
  transition,
100
102
  pointerEvents: open.value ? 'auto' : 'none',
103
+ zIndex: '1000',
101
104
  }
102
105
  }
103
106
 
104
- // Linear directions: offset from the container edge
105
107
  const step = ITEM_PX + ITEM_GAP
106
- const base = fabPx.value + ITEM_GAP + index * step
108
+ const offset = fabPx.value / 2 + ITEM_GAP + ITEM_PX / 2 + index * step
107
109
 
108
- const offsetMap: Record<string, Record<string, string>> = {
109
- up: { bottom: `${base}px`, left: '50%', marginLeft: `${-ITEM_PX / 2}px` },
110
- down: { top: `${base}px`, left: '50%', marginLeft: `${-ITEM_PX / 2}px` },
111
- left: { right: `${base}px`, top: '50%', marginTop: `${-ITEM_PX / 2}px` },
112
- right: { left: `${base}px`, top: '50%', marginTop: `${-ITEM_PX / 2}px` },
110
+ const posMap: Record<string, { top: string; left: string }> = {
111
+ up: { top: `${cy - offset - ITEM_PX / 2}px`, left: `${cx - ITEM_PX / 2}px` },
112
+ down: { top: `${cy + offset - ITEM_PX / 2}px`, left: `${cx - ITEM_PX / 2}px` },
113
+ left: { top: `${cy - ITEM_PX / 2}px`, left: `${cx - offset - ITEM_PX / 2}px` },
114
+ right: { top: `${cy - ITEM_PX / 2}px`, left: `${cx + offset - ITEM_PX / 2}px` },
113
115
  }
114
116
 
115
117
  const translateFrom: Record<string, string> = {
@@ -119,19 +121,27 @@ function itemStyle(index: number): Record<string, string> {
119
121
  right: 'translateX(-12px) scale(0.75)',
120
122
  }
121
123
 
124
+ const pos = posMap[props.direction] ?? posMap.up
125
+
122
126
  return {
123
- position: 'absolute',
124
- ...offsetMap[props.direction] ?? offsetMap.up,
127
+ position: 'fixed',
128
+ ...pos,
125
129
  transform: open.value ? 'translate(0,0) scale(1)' : (translateFrom[props.direction] ?? 'scale(0.75)'),
126
130
  opacity: open.value ? '1' : '0',
127
131
  transition,
128
132
  pointerEvents: open.value ? 'auto' : 'none',
133
+ zIndex: '1000',
129
134
  }
130
135
  }
131
136
 
132
- // Label only makes sense for up/down; placed to the left of the item button
133
137
  const showLabel = computed(() => props.direction === 'up' || props.direction === 'down')
134
138
 
139
+ // Force re-render to recalculate positions on scroll
140
+ const scrollTick = ref(0)
141
+ function onScroll() {
142
+ if (open.value) scrollTick.value++
143
+ }
144
+
135
145
  function createRipple(event: PointerEvent | MouseEvent, target?: HTMLElement) {
136
146
  const button = (target ?? event.currentTarget) as HTMLElement
137
147
  const rect = button.getBoundingClientRect()
@@ -159,19 +169,44 @@ function handleItemClick(e: PointerEvent, item: SpeedDialItem, buttonEl: HTMLEle
159
169
 
160
170
  function onDocClick(e: MouseEvent) {
161
171
  if (!open.value) return
162
- if (containerEl.value && !containerEl.value.contains(e.target as Node)) {
172
+ if (fabEl.value && !fabEl.value.contains(e.target as Node)) {
163
173
  open.value = false
164
174
  }
165
175
  }
166
176
 
167
- onMounted(() => document.addEventListener('click', onDocClick, true))
168
- onUnmounted(() => document.removeEventListener('click', onDocClick, true))
177
+ onMounted(() => {
178
+ document.addEventListener('click', onDocClick, true)
179
+ window.addEventListener('scroll', onScroll, true)
180
+ })
181
+ onUnmounted(() => {
182
+ document.removeEventListener('click', onDocClick, true)
183
+ window.removeEventListener('scroll', onScroll, true)
184
+ })
169
185
  </script>
170
186
 
171
187
  <template>
172
- <div ref="containerEl" class="relative inline-flex items-center justify-center">
173
- <!-- Speed-dial items (absolutely positioned outside the container) -->
188
+ <div ref="fabEl" class="relative inline-flex items-center justify-center">
189
+ <button
190
+ type="button"
191
+ class="relative inline-flex cursor-pointer items-center justify-center overflow-hidden shadow-elevation-1 transition-shadow duration-150 hover:shadow-elevation-2 active:shadow-elevation-1 disabled:cursor-not-allowed disabled:opacity-[0.38] before:content-[''] before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 hover:before:opacity-[0.08] active:before:opacity-[0.12]"
192
+ :class="[colorMap[color], fabSizeClasses]"
193
+ :disabled="disabled"
194
+ @pointerdown="(e) => { createRipple(e); handleFabClick(e) }"
195
+ >
196
+ <MIcon
197
+ :name="icon"
198
+ :size="fabIconSize"
199
+ class="transition-transform duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)]"
200
+ :class="hasItems && open ? 'rotate-45' : ''"
201
+ />
202
+ <span v-if="label" class="text-label-large font-medium">{{ label }}</span>
203
+ </button>
204
+ </div>
205
+
206
+ <Teleport to="body">
174
207
  <template v-if="hasItems">
208
+ <!-- hidden dep on scrollTick to force style recalc -->
209
+ <span :data-tick="scrollTick" class="hidden" />
175
210
  <div
176
211
  v-for="(item, i) in items"
177
212
  :key="i"
@@ -179,7 +214,6 @@ onUnmounted(() => document.removeEventListener('click', onDocClick, true))
179
214
  class="flex items-center gap-3"
180
215
  :class="showLabel ? 'flex-row-reverse' : ''"
181
216
  >
182
- <!-- Label pill (up/down only) -->
183
217
  <span
184
218
  v-if="item.label && showLabel"
185
219
  class="whitespace-nowrap rounded-md bg-surface-container-high px-3 py-1.5 text-label-medium text-on-surface shadow-elevation-1"
@@ -187,7 +221,6 @@ onUnmounted(() => document.removeEventListener('click', onDocClick, true))
187
221
  {{ item.label }}
188
222
  </span>
189
223
 
190
- <!-- Mini FAB button -->
191
224
  <button
192
225
  type="button"
193
226
  class="relative flex cursor-pointer items-center justify-center overflow-hidden rounded-lg shadow-elevation-1 transition-shadow duration-150 hover:shadow-elevation-2 active:shadow-elevation-1 before:content-[''] before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 hover:before:opacity-[0.08] active:before:opacity-[0.12]"
@@ -199,22 +232,5 @@ onUnmounted(() => document.removeEventListener('click', onDocClick, true))
199
232
  </button>
200
233
  </div>
201
234
  </template>
202
-
203
- <!-- Main FAB -->
204
- <button
205
- type="button"
206
- class="relative inline-flex cursor-pointer items-center justify-center overflow-hidden shadow-elevation-1 transition-shadow duration-150 hover:shadow-elevation-2 active:shadow-elevation-1 disabled:cursor-not-allowed disabled:opacity-[0.38] before:content-[''] before:pointer-events-none before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity before:duration-150 hover:before:opacity-[0.08] active:before:opacity-[0.12]"
207
- :class="[colorMap[color], fabSizeClasses]"
208
- :disabled="disabled"
209
- @pointerdown="(e) => { createRipple(e); handleFabClick(e) }"
210
- >
211
- <MIcon
212
- :name="icon"
213
- :size="fabIconSize"
214
- class="transition-transform duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)]"
215
- :class="hasItems && open ? 'rotate-45' : ''"
216
- />
217
- <span v-if="label" class="text-label-large font-medium">{{ label }}</span>
218
- </button>
219
- </div>
235
+ </Teleport>
220
236
  </template>
@@ -147,7 +147,8 @@ const triggerClasses = computed(() => {
147
147
 
148
148
  return [
149
149
  ...base,
150
- 'rounded-t-sm bg-surface-container-highest border-b pt-7 pb-2',
150
+ 'rounded-t-sm bg-surface-container-highest border-b pb-2',
151
+ hasValue.value || open.value ? 'pt-7' : 'pt-4',
151
152
  open.value
152
153
  ? (props.error ? 'border-b-2 border-error' : 'border-b-2 border-primary')
153
154
  : (props.error ? 'border-error' : 'border-on-surface-variant hover:border-on-surface'),
@@ -161,7 +162,7 @@ const labelClasses = computed(() => {
161
162
 
162
163
  const floated = props.variant === 'outlined'
163
164
  ? '-top-2.5 translate-y-0 text-label-small bg-[var(--field-bg)] px-1 right-auto max-w-[calc(100%-1.5rem)]'
164
- : 'top-3.5 translate-y-0 text-label-small'
165
+ : 'top-2 translate-y-0 text-label-small'
165
166
 
166
167
  const unFloated = 'top-1/2 -translate-y-1/2 text-body-large'
167
168
  const active = open.value || hasValue.value
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { nextTick, ref } from 'vue'
2
+ import { nextTick, onMounted, onUnmounted, ref } from 'vue'
3
3
 
4
4
  const props = withDefaults(defineProps<{
5
5
  text: string
@@ -27,6 +27,10 @@ function hide() {
27
27
  visible.value = false
28
28
  }
29
29
 
30
+ function onScroll() {
31
+ if (visible.value) hide()
32
+ }
33
+
30
34
  function reposition() {
31
35
  if (!triggerEl.value || !tipEl.value) return
32
36
  const tr = triggerEl.value.getBoundingClientRect()
@@ -45,6 +49,9 @@ function reposition() {
45
49
  left = Math.max(6, Math.min(left, window.innerWidth - tt.width - 6))
46
50
  tipStyle.value = { top: `${top}px`, left: `${left}px` }
47
51
  }
52
+
53
+ onMounted(() => window.addEventListener('scroll', onScroll, true))
54
+ onUnmounted(() => window.removeEventListener('scroll', onScroll, true))
48
55
  </script>
49
56
 
50
57
  <template>
package/src/index.ts CHANGED
@@ -21,10 +21,10 @@ export { default as MBreadcrumbs } from './components/MBreadcrumbs.vue'
21
21
  export { default as MButton } from './components/MButton.vue'
22
22
  export { default as MCalendar } from './components/MCalendar.vue'
23
23
  export { default as MCard } from './components/MCard.vue'
24
- export { default as MChart } from './components/MChart.vue'
24
+ // MChart import from '@m3ui-vue/m3ui-vue/chart'
25
25
  export { default as MCheckbox } from './components/MCheckbox.vue'
26
26
  export { default as MChip } from './components/MChip.vue'
27
- export { default as MCodeEditor } from './components/MCodeEditor.vue'
27
+ // MCodeEditor import from '@m3ui-vue/m3ui-vue/code-editor'
28
28
  export { default as MColorPicker } from './components/MColorPicker.vue'
29
29
  export { default as MCommandPalette } from './components/MCommandPalette.vue'
30
30
  export { default as MConfirmDialog } from './components/MConfirmDialog.vue'
@@ -45,11 +45,11 @@ export { default as MHotkeys } from './components/MHotkeys.vue'
45
45
  export { default as MIcon } from './components/MIcon.vue'
46
46
  export { default as MIconButton } from './components/MIconButton.vue'
47
47
  export { default as MInfiniteScroll } from './components/MInfiniteScroll.vue'
48
- export { default as MJsonEditor } from './components/MJsonEditor.vue'
48
+ // MJsonEditor import from '@m3ui-vue/m3ui-vue/code-editor'
49
49
  export { default as MJsonViewer } from './components/MJsonViewer.vue'
50
50
  export { default as MKanban } from './components/MKanban.vue'
51
51
  export { default as MLoadingOverlay } from './components/MLoadingOverlay.vue'
52
- export { default as MMarkdown } from './components/MMarkdown.vue'
52
+ // MMarkdown import from '@m3ui-vue/m3ui-vue/markdown'
53
53
  export { default as MMasonry } from './components/MMasonry.vue'
54
54
  export { default as MMenu } from './components/MMenu.vue'
55
55
  export { default as MMenuItem } from './components/MMenuItem.vue'
@@ -63,7 +63,7 @@ export { default as MRadio } from './components/MRadio.vue'
63
63
  export { default as MRadioGroup } from './components/MRadioGroup.vue'
64
64
  export { default as MRating } from './components/MRating.vue'
65
65
  export { default as MResult } from './components/MResult.vue'
66
- export { default as MRichTextEditor } from './components/MRichTextEditor.vue'
66
+ // MRichTextEditor import from '@m3ui-vue/m3ui-vue/rich-text-editor'
67
67
  export { default as MScheduler } from './components/MScheduler.vue'
68
68
  export { default as MSegmentedButton } from './components/MSegmentedButton.vue'
69
69
  export { default as MSelect } from './components/MSelect.vue'
@@ -80,7 +80,7 @@ export { default as MStepper } from './components/MStepper.vue'
80
80
  export { default as MSwitch } from './components/MSwitch.vue'
81
81
  export { default as MTable } from './components/MTable.vue'
82
82
  export { default as MTabs } from './components/MTabs.vue'
83
- export { default as MTerminal } from './components/MTerminal.vue'
83
+ // MTerminal import from '@m3ui-vue/m3ui-vue/terminal'
84
84
  export { default as MTextField } from './components/MTextField.vue'
85
85
  export { default as MTimeline } from './components/MTimeline.vue'
86
86
  export { default as MTimePicker } from './components/MTimePicker.vue'
@@ -0,0 +1 @@
1
+ export { default as MMarkdown } from './components/MMarkdown.vue'
@@ -0,0 +1 @@
1
+ export { default as MRichTextEditor } from './components/MRichTextEditor.vue'
@@ -1,3 +1,6 @@
1
+ /* Scan the library's components so Tailwind generates the needed utilities */
2
+ @source '..';
3
+
1
4
  /* Enable class-based dark mode for Tailwind v4 */
2
5
  @custom-variant dark (&:where(.dark, .dark *));
3
6
 
@@ -0,0 +1 @@
1
+ export { default as MTerminal } from './components/MTerminal.vue'
package/dist/m3ui.css DELETED
@@ -1,2 +0,0 @@
1
- .bs-scrim[data-v-941a89e7]{transition:opacity .3s}.bs-enter-from .bs-scrim[data-v-941a89e7],.bs-leave-to .bs-scrim[data-v-941a89e7]{opacity:0}.bs-panel[data-v-941a89e7]{transition:transform .32s cubic-bezier(.2,0,0,1),opacity .24s}.bs-enter-from .bs-panel[data-v-941a89e7]{opacity:0;transform:translateY(40%)}.bs-leave-to .bs-panel[data-v-941a89e7]{opacity:0;transform:translateY(100%)!important}@keyframes m3-wavy-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes m3-wavy-travel{0%{stroke-dashoffset:0}to{stroke-dashoffset:calc(var(--m3-wave-len) * -1px)}}@media (prefers-reduced-motion:reduce){.animate-\[m3-wavy-spin_2\.8s_linear_infinite\]{animation:2.8s linear infinite m3-wavy-spin}.animate-\[m3-wavy-travel_2s_linear_infinite\]{animation:none!important}}.code-editor-container[data-v-5274a428] .cm-editor{height:100%;min-height:inherit;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:.875rem}.code-editor-container[data-v-5274a428] .cm-editor.cm-focused{outline:none}.code-editor-container[data-v-5274a428] .cm-scroller{min-height:inherit}.code-editor-container[data-v-5274a428] .cm-gutters{background:var(--color-surface-container);border-right:1px solid var(--color-outline-variant);color:var(--color-on-surface-variant)}.code-editor-container[data-v-5274a428] .cm-activeLineGutter{background:var(--color-surface-container-high)}.code-editor-container[data-v-5274a428] .cm-activeLine{background:var(--color-surface-container-lowest)}.code-editor-container[data-v-5274a428] .cm-selectionBackground{background:var(--color-primary-container)!important}.code-editor-container[data-v-5274a428] .cm-cursor{border-left-color:var(--color-primary)}.code-editor-container[data-v-5274a428] .cm-matchingBracket{background:var(--color-tertiary-container);color:var(--color-on-tertiary-container)}.code-editor-container[data-v-5274a428] .cm-foldGutter span{color:var(--color-on-surface-variant)}.hue-slider[data-v-9ee0043f]{background:linear-gradient(90deg,red 0%,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}.hue-slider[data-v-9ee0043f]::-webkit-slider-thumb{-webkit-appearance:none;cursor:pointer;background:#fff;border-radius:50%;width:16px;height:16px;box-shadow:0 1px 3px #0006}.hue-slider[data-v-9ee0043f]::-moz-range-thumb{cursor:pointer;background:#fff;border:none;border-radius:50%;width:16px;height:16px;box-shadow:0 1px 3px #0006}.m3-cmd-enter-active[data-v-da578f14],.m3-cmd-leave-active[data-v-da578f14]{transition:opacity .15s}.m3-cmd-enter-from[data-v-da578f14],.m3-cmd-leave-to[data-v-da578f14]{opacity:0}.m3-cmd-enter-active .cmd-box[data-v-da578f14],.m3-cmd-leave-active .cmd-box[data-v-da578f14]{transition:transform .15s}.m3-cmd-enter-from .cmd-box[data-v-da578f14],.m3-cmd-leave-to .cmd-box[data-v-da578f14]{transform:scale(.95)translateY(-10px)}.m3-dialog-enter-active[data-v-e7dfca29],.m3-dialog-leave-active[data-v-e7dfca29]{transition:opacity .15s}.m3-dialog-enter-from[data-v-e7dfca29],.m3-dialog-leave-to[data-v-e7dfca29]{opacity:0}.m3-dialog-enter-active .dialog-box[data-v-e7dfca29],.m3-dialog-leave-active .dialog-box[data-v-e7dfca29]{transition:transform .15s}.m3-dialog-enter-from .dialog-box[data-v-e7dfca29],.m3-dialog-leave-to .dialog-box[data-v-e7dfca29]{transform:scale(.95)}.expand-grid[data-v-89e4475b]{grid-template-rows:1fr;display:grid}.expand-body[data-v-89e4475b]{min-height:0;overflow:hidden}.expand-enter-active[data-v-89e4475b]{transition:grid-template-rows .28s cubic-bezier(.2,0,0,1)}.expand-enter-active>.expand-body[data-v-89e4475b]{transition:opacity .22s}.expand-enter-from[data-v-89e4475b]{grid-template-rows:0fr}.expand-enter-from>.expand-body[data-v-89e4475b]{opacity:0}.expand-leave-active[data-v-89e4475b]{transition:grid-template-rows .22s cubic-bezier(.4,0,1,1)}.expand-leave-active>.expand-body[data-v-89e4475b]{transition:opacity .15s}.expand-leave-to[data-v-89e4475b]{grid-template-rows:0fr}.expand-leave-to>.expand-body[data-v-89e4475b]{opacity:0}.m3-file-enter-active[data-v-34a862f0],.m3-file-leave-active[data-v-34a862f0]{transition:all .2s}.m3-file-enter-from[data-v-34a862f0],.m3-file-leave-to[data-v-34a862f0]{opacity:0;transform:translateY(-8px)}.m3-markdown[data-v-6e4dc2b6] h1{font-size:var(--text-headline-large);line-height:var(--text-headline-large--line-height);color:var(--color-on-surface);margin:1em 0 .5em;font-weight:600}.m3-markdown[data-v-6e4dc2b6] h2{font-size:var(--text-headline-medium);line-height:var(--text-headline-medium--line-height);color:var(--color-on-surface);margin:1em 0 .5em;font-weight:600}.m3-markdown[data-v-6e4dc2b6] h3{font-size:var(--text-headline-small);line-height:var(--text-headline-small--line-height);color:var(--color-on-surface);margin:.75em 0 .25em;font-weight:600}.m3-markdown[data-v-6e4dc2b6] h4{font-size:var(--text-title-large);line-height:var(--text-title-large--line-height);color:var(--color-on-surface);margin:.75em 0 .25em;font-weight:600}.m3-markdown[data-v-6e4dc2b6] p{margin:.5em 0}.m3-markdown[data-v-6e4dc2b6] a{color:var(--color-primary);text-underline-offset:2px;text-decoration:underline}.m3-markdown[data-v-6e4dc2b6] a:hover{opacity:.8}.m3-markdown[data-v-6e4dc2b6] strong{color:var(--color-on-surface);font-weight:600}.m3-markdown[data-v-6e4dc2b6] em{font-style:italic}.m3-markdown[data-v-6e4dc2b6] ul,.m3-markdown[data-v-6e4dc2b6] ol{margin:.5em 0;padding-left:1.5em}.m3-markdown[data-v-6e4dc2b6] li{margin:.25em 0}.m3-markdown[data-v-6e4dc2b6] li::marker{color:var(--color-on-surface-variant)}.m3-markdown[data-v-6e4dc2b6] blockquote{border-left:3px solid var(--color-primary);background:var(--color-surface-container);color:var(--color-on-surface-variant);border-radius:0 8px 8px 0;margin:.75em 0;padding:.5em 1em}.m3-markdown[data-v-6e4dc2b6] code{background:var(--color-surface-container-highest);color:var(--color-primary);border-radius:4px;padding:.15em .4em;font-family:JetBrains Mono,Fira Code,monospace;font-size:.875em}.m3-markdown[data-v-6e4dc2b6] pre{background:var(--color-surface-container-highest);border:1px solid var(--color-outline-variant);border-radius:12px;margin:.75em 0;padding:1em;overflow-x:auto}.m3-markdown[data-v-6e4dc2b6] pre code{color:var(--color-on-surface);background:0 0;padding:0}.m3-markdown[data-v-6e4dc2b6] hr{border:none;border-top:1px solid var(--color-outline-variant);margin:1.5em 0}.m3-markdown[data-v-6e4dc2b6] table{border-collapse:collapse;width:100%;margin:.75em 0}.m3-markdown[data-v-6e4dc2b6] th{background:var(--color-surface-container);text-align:left;border-bottom:2px solid var(--color-outline-variant);font-weight:600;font-size:var(--text-label-large);color:var(--color-on-surface);padding:.5em .75em}.m3-markdown[data-v-6e4dc2b6] td{border-bottom:1px solid var(--color-outline-variant);padding:.5em .75em}.m3-markdown[data-v-6e4dc2b6] img{border-radius:12px;max-width:100%;height:auto;margin:.5em 0}.nd-scrim[data-v-98c11a62]{transition:opacity .28s}.nd-enter-from .nd-scrim[data-v-98c11a62],.nd-leave-to .nd-scrim[data-v-98c11a62]{opacity:0}.nd-panel[data-v-98c11a62]{transition:transform .3s cubic-bezier(.2,0,0,1)}.nd-enter-from .nd-panel[data-v-98c11a62],.nd-leave-to .nd-panel[data-v-98c11a62]{transform:translate(-100%)}@keyframes m3-wave-flow{0%{transform:translate(0)}to{transform:translate(-20px)}}@keyframes m3-progress-indeterminate{0%{left:-40%}to{left:100%}}@media (prefers-reduced-motion:reduce){.animate-\[m3-wave-flow_1\.2s_linear_infinite\],.animate-\[m3-wave-flow_0\.9s_linear_infinite\],.animate-\[m3-progress-indeterminate_1\.6s_ease-in-out_infinite\]{animation:none!important}}.m3-radio-dot[data-v-cdb650b5]{transform-box:fill-box;transform-origin:50%;transition:transform .15s;transform:scale(0)}.m3-radio-dot.is-checked[data-v-cdb650b5]{transform:scale(1)}.rte-content[data-v-89c08c83] .tiptap{min-height:inherit;outline:none}.rte-content[data-v-89c08c83] .tiptap p.is-editor-empty:first-child:before{content:attr(data-placeholder);float:left;color:var(--color-on-surface-variant);opacity:.5;pointer-events:none;height:0}.rte-content[data-v-89c08c83] h1{font-size:var(--text-headline-large);line-height:var(--text-headline-large--line-height);margin:.75em 0 .25em;font-weight:600}.rte-content[data-v-89c08c83] h2{font-size:var(--text-headline-medium);line-height:var(--text-headline-medium--line-height);margin:.75em 0 .25em;font-weight:600}.rte-content[data-v-89c08c83] h3{font-size:var(--text-headline-small);line-height:var(--text-headline-small--line-height);margin:.75em 0 .25em;font-weight:600}.rte-content[data-v-89c08c83] p{margin:.5em 0}.rte-content[data-v-89c08c83] ul,.rte-content[data-v-89c08c83] ol{margin:.5em 0;padding-left:1.5em}.rte-content[data-v-89c08c83] blockquote{border-left:3px solid var(--color-primary);color:var(--color-on-surface-variant);margin:.5em 0;padding-left:1em}.rte-content[data-v-89c08c83] code{background:var(--color-surface-container-highest);border-radius:4px;padding:.15em .4em;font-size:.875em}.rte-content[data-v-89c08c83] pre{background:var(--color-surface-container-highest);border-radius:8px;margin:.5em 0;padding:1em;overflow-x:auto}.rte-content[data-v-89c08c83] pre code{background:0 0;padding:0}.rte-content[data-v-89c08c83] a{color:var(--color-primary);text-decoration:underline}.rte-content[data-v-89c08c83] mark{background:var(--color-tertiary-container);color:var(--color-on-tertiary-container);border-radius:2px;padding:.1em .2em}.rte-content[data-v-89c08c83] img{border-radius:8px;max-width:100%;height:auto;margin:.5em 0}.ss-scrim[data-v-f8751672]{transition:opacity .28s}.ss-enter-from .ss-scrim[data-v-f8751672],.ss-leave-to .ss-scrim[data-v-f8751672]{opacity:0}.ss-panel[data-v-f8751672]{transition:transform .32s cubic-bezier(.2,0,0,1),opacity .24s}.ss-enter-from .ss-panel[data-v-f8751672]{opacity:0;transform:translate(40%)}.ss-leave-to .ss-panel[data-v-f8751672]{opacity:0;transform:translate(100%)!important}@keyframes skeleton-wave-move-32ecf05b{0%{transform:translate(-100%)}60%{transform:translate(100%)}to{transform:translate(100%)}}.skeleton-wave[data-v-32ecf05b]{position:relative;overflow:hidden}.skeleton-wave[data-v-32ecf05b]:after{content:"";background:linear-gradient(90deg, transparent 0%, var(--color-on-surface) 50%, transparent 100%);opacity:.06;animation:1.8s ease-in-out infinite skeleton-wave-move-32ecf05b;position:absolute;inset:0}.toast-row[data-v-e83a5c10]{grid-template-rows:1fr;padding-bottom:8px;display:grid}.toast-row>.toast-inner[data-v-e83a5c10]{min-height:0}.m3-toast-bot-enter-active[data-v-e83a5c10]{transition:grid-template-rows .22s cubic-bezier(.2,0,0,1),padding-bottom .22s cubic-bezier(.2,0,0,1);overflow:hidden}.m3-toast-bot-enter-active>.toast-inner[data-v-e83a5c10]{transition:opacity .18s,transform .22s cubic-bezier(.2,0,0,1)}.m3-toast-bot-enter-from[data-v-e83a5c10]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-bot-enter-from>.toast-inner[data-v-e83a5c10]{opacity:0;transform:translateY(20px)scale(.94)}.m3-toast-bot-leave-active[data-v-e83a5c10]{transition:grid-template-rows .3s cubic-bezier(.2,0,0,1),padding-bottom .3s cubic-bezier(.2,0,0,1);overflow:hidden}.m3-toast-bot-leave-active>.toast-inner[data-v-e83a5c10]{transition:opacity .18s,transform .18s}.m3-toast-bot-leave-to[data-v-e83a5c10]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-bot-leave-to>.toast-inner[data-v-e83a5c10]{opacity:0;transform:scale(.92)}.m3-toast-top-enter-active[data-v-e83a5c10]{transition:grid-template-rows .22s cubic-bezier(.2,0,0,1),padding-bottom .22s cubic-bezier(.2,0,0,1);overflow:hidden}.m3-toast-top-enter-active>.toast-inner[data-v-e83a5c10]{transition:opacity .18s,transform .22s cubic-bezier(.2,0,0,1)}.m3-toast-top-enter-from[data-v-e83a5c10]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-top-enter-from>.toast-inner[data-v-e83a5c10]{opacity:0;transform:translateY(-20px)scale(.94)}.m3-toast-top-leave-active[data-v-e83a5c10]{transition:grid-template-rows .3s cubic-bezier(.2,0,0,1),padding-bottom .3s cubic-bezier(.2,0,0,1);overflow:hidden}.m3-toast-top-leave-active>.toast-inner[data-v-e83a5c10]{transition:opacity .18s,transform .18s}.m3-toast-top-leave-to[data-v-e83a5c10]{grid-template-rows:0fr;padding-bottom:0}.m3-toast-top-leave-to>.toast-inner[data-v-e83a5c10]{opacity:0;transform:scale(.92)}@keyframes m3-toast-progress-e83a5c10{0%{transform:scaleX(1)}to{transform:scaleX(0)}}.m3-spot-enter-active[data-v-51b103ff],.m3-spot-leave-active[data-v-51b103ff]{transition:opacity .15s}.m3-spot-enter-from[data-v-51b103ff],.m3-spot-leave-to[data-v-51b103ff]{opacity:0}.m3-spot-enter-active .spot-box[data-v-51b103ff],.m3-spot-leave-active .spot-box[data-v-51b103ff]{transition:transform .15s,opacity .15s}.m3-spot-enter-from .spot-box[data-v-51b103ff]{opacity:0;transform:scale(.96)translateY(-8px)}.m3-spot-leave-to .spot-box[data-v-51b103ff]{opacity:0;transform:scale(.98)}.m3-tour-highlight{box-shadow:0 0 0 4px var(--color-primary), 0 0 0 9999px #0006;border-radius:8px;position:relative;z-index:101!important}.m3-tour-enter-active,.m3-tour-leave-active{transition:opacity .2s}.m3-tour-enter-from,.m3-tour-leave-to{opacity:0}
2
- /*$vite$:1*/