@morscherlab/mld-sdk 0.9.7 → 0.10.0

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 (56) hide show
  1. package/dist/__tests__/composables/useAppExperiment.test.d.ts +1 -0
  2. package/dist/components/AppTopBar.vue.js +61 -28
  3. package/dist/components/AppTopBar.vue.js.map +1 -1
  4. package/dist/components/AuditTrail.vue.d.ts +1 -1
  5. package/dist/components/ExperimentSelectorModal.vue.d.ts +1 -1
  6. package/dist/components/FormulaInput.vue.js +24 -18
  7. package/dist/components/FormulaInput.vue.js.map +1 -1
  8. package/dist/components/MoleculeInput.vue.js +15 -6
  9. package/dist/components/MoleculeInput.vue.js.map +1 -1
  10. package/dist/components/PlateMapEditor.vue.js +1 -1
  11. package/dist/components/PlateMapEditor.vue.js.map +1 -1
  12. package/dist/components/ScientificNumber.vue.d.ts +1 -1
  13. package/dist/composables/index.d.ts +1 -0
  14. package/dist/composables/index.js +3 -0
  15. package/dist/composables/index.js.map +1 -1
  16. package/dist/composables/useAppExperiment.d.ts +34 -0
  17. package/dist/composables/useAppExperiment.js +91 -0
  18. package/dist/composables/useAppExperiment.js.map +1 -0
  19. package/dist/composables/useAuth.js +26 -25
  20. package/dist/composables/useAuth.js.map +1 -1
  21. package/dist/composables/useAutoGroup.js +7 -32
  22. package/dist/composables/useAutoGroup.js.map +1 -1
  23. package/dist/composables/useForm.js +1 -1
  24. package/dist/composables/useForm.js.map +1 -1
  25. package/dist/composables/usePlatformContext.js +8 -1
  26. package/dist/composables/usePlatformContext.js.map +1 -1
  27. package/dist/composables/useTheme.js +23 -25
  28. package/dist/composables/useTheme.js.map +1 -1
  29. package/dist/composables/useWellPlateEditor.d.ts +1 -0
  30. package/dist/composables/useWellPlateEditor.js +21 -10
  31. package/dist/composables/useWellPlateEditor.js.map +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.js +3 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/stores/auth.d.ts +1 -1
  36. package/dist/stores/settings.d.ts +1 -1
  37. package/dist/stores/settings.js +17 -30
  38. package/dist/stores/settings.js.map +1 -1
  39. package/dist/styles.css +6920 -6908
  40. package/package.json +1 -1
  41. package/src/__tests__/composables/useAppExperiment.test.ts +560 -0
  42. package/src/components/AppTopBar.vue +38 -2
  43. package/src/components/FormulaInput.vue +17 -16
  44. package/src/components/MoleculeInput.vue +29 -14
  45. package/src/components/PlateMapEditor.vue +1 -1
  46. package/src/composables/index.ts +7 -0
  47. package/src/composables/useAppExperiment.ts +143 -0
  48. package/src/composables/useAuth.ts +29 -31
  49. package/src/composables/useAutoGroup.ts +7 -33
  50. package/src/composables/useForm.ts +1 -1
  51. package/src/composables/usePlatformContext.ts +7 -1
  52. package/src/composables/useTheme.ts +33 -28
  53. package/src/composables/useWellPlateEditor.ts +22 -10
  54. package/src/index.ts +5 -0
  55. package/src/stores/settings.ts +22 -38
  56. package/src/styles/components/formula-input.css +13 -6
@@ -1,9 +1,14 @@
1
- import { defineComponent, ref, computed, onMounted, onUnmounted, resolveComponent, openBlock, createElementBlock, Fragment, createElementVNode, normalizeClass, renderSlot, createCommentVNode, createBlock, withCtx, toDisplayString, withModifiers, createTextVNode, withDirectives, renderList, vShow, createSlots } from "vue";
2
- import _sfc_main$1 from "./ThemeToggle.vue.js";
1
+ import { defineComponent, ref, computed, inject, onMounted, onUnmounted, resolveComponent, openBlock, createElementBlock, Fragment, createElementVNode, normalizeClass, renderSlot, createCommentVNode, createBlock, withCtx, toDisplayString, withModifiers, createTextVNode, withDirectives, renderList, vShow, unref, createSlots } from "vue";
2
+ import _sfc_main$2 from "./ThemeToggle.vue.js";
3
3
  /* empty css */
4
- import _sfc_main$2 from "./SettingsModal.vue.js";
4
+ import _sfc_main$3 from "./SettingsModal.vue.js";
5
5
  /* empty css */
6
+ import _sfc_main$1 from "./ExperimentPopover.vue.js";
7
+ /* empty css */
8
+ import _sfc_main$4 from "./ExperimentSelectorModal.vue.js";
9
+ /* empty css */
6
10
  import { usePlatformContext } from "../composables/usePlatformContext.js";
11
+ import { APP_EXPERIMENT_KEY } from "../composables/useAppExperiment.js";
7
12
  const _hoisted_1 = { class: "mld-topbar__container" };
8
13
  const _hoisted_2 = { class: "mld-topbar__left" };
9
14
  const _hoisted_3 = ["href"];
@@ -105,8 +110,12 @@ const _hoisted_39 = {
105
110
  key: 0,
106
111
  class: "mld-topbar__standalone-badge"
107
112
  };
108
- const _hoisted_40 = { class: "mld-topbar__profile-avatar" };
109
- const _hoisted_41 = {
113
+ const _hoisted_40 = {
114
+ key: 2,
115
+ class: "mld-topbar__standalone-badge"
116
+ };
117
+ const _hoisted_41 = { class: "mld-topbar__profile-avatar" };
118
+ const _hoisted_42 = {
110
119
  key: 0,
111
120
  class: "mld-topbar__profile-name"
112
121
  };
@@ -140,6 +149,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
140
149
  const settingsOpen = ref(false);
141
150
  const { isIntegrated } = usePlatformContext();
142
151
  const isStandalone = computed(() => !isIntegrated.value);
152
+ const appExperiment = inject(APP_EXPERIMENT_KEY, null);
143
153
  const profileInitial = computed(() => {
144
154
  if (props.userInitial) return props.userInitial;
145
155
  if (props.userName) return props.userName.charAt(0).toUpperCase();
@@ -222,7 +232,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
222
232
  }, [
223
233
  renderSlot(_ctx.$slots, "icon", {}, () => [
224
234
  renderSlot(_ctx.$slots, "logo", {}, () => [
225
- __props.showLogo ? (openBlock(), createElementBlock("div", _hoisted_4, [..._cache[8] || (_cache[8] = [
235
+ __props.showLogo ? (openBlock(), createElementBlock("div", _hoisted_4, [..._cache[14] || (_cache[14] = [
226
236
  createElementVNode("div", { class: "mld-topbar__logo-icon" }, [
227
237
  createElementVNode("span", { class: "mld-topbar__logo-text" }, "M")
228
238
  ], -1)
@@ -236,7 +246,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
236
246
  }, [
237
247
  renderSlot(_ctx.$slots, "icon", {}, () => [
238
248
  renderSlot(_ctx.$slots, "logo", {}, () => [
239
- __props.showLogo ? (openBlock(), createElementBlock("div", _hoisted_6, [..._cache[9] || (_cache[9] = [
249
+ __props.showLogo ? (openBlock(), createElementBlock("div", _hoisted_6, [..._cache[15] || (_cache[15] = [
240
250
  createElementVNode("div", { class: "mld-topbar__logo-icon" }, [
241
251
  createElementVNode("span", { class: "mld-topbar__logo-text" }, "M")
242
252
  ], -1)
@@ -251,7 +261,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
251
261
  default: withCtx(() => [
252
262
  renderSlot(_ctx.$slots, "icon", {}, () => [
253
263
  renderSlot(_ctx.$slots, "logo", {}, () => [
254
- __props.showLogo ? (openBlock(), createElementBlock("div", _hoisted_7, [..._cache[10] || (_cache[10] = [
264
+ __props.showLogo ? (openBlock(), createElementBlock("div", _hoisted_7, [..._cache[16] || (_cache[16] = [
255
265
  createElementVNode("div", { class: "mld-topbar__logo-icon" }, [
256
266
  createElementVNode("span", { class: "mld-topbar__logo-text" }, "M")
257
267
  ], -1)
@@ -262,7 +272,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
262
272
  _: 3
263
273
  }, 8, ["to"])) : renderSlot(_ctx.$slots, "icon", { key: 3 }, () => [
264
274
  renderSlot(_ctx.$slots, "logo", {}, () => [
265
- __props.showLogo ? (openBlock(), createElementBlock("div", _hoisted_8, [..._cache[11] || (_cache[11] = [
275
+ __props.showLogo ? (openBlock(), createElementBlock("div", _hoisted_8, [..._cache[17] || (_cache[17] = [
266
276
  createElementVNode("div", { class: "mld-topbar__logo-icon" }, [
267
277
  createElementVNode("span", { class: "mld-topbar__logo-text" }, "M")
268
278
  ], -1)
@@ -295,11 +305,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
295
305
  "stroke-width": "2",
296
306
  "stroke-linecap": "round",
297
307
  "stroke-linejoin": "round"
298
- }, [..._cache[12] || (_cache[12] = [
308
+ }, [..._cache[18] || (_cache[18] = [
299
309
  createElementVNode("path", { d: "m6 9 6 6 6-6" }, null, -1)
300
310
  ])], 2))
301
311
  ])) : (openBlock(), createElementBlock("span", _hoisted_12, toDisplayString(__props.pluginName), 1)),
302
- __props.title ? (openBlock(), createElementBlock("svg", _hoisted_13, [..._cache[13] || (_cache[13] = [
312
+ __props.title ? (openBlock(), createElementBlock("svg", _hoisted_13, [..._cache[19] || (_cache[19] = [
303
313
  createElementVNode("path", { d: "m9 18 6-6-6-6" }, null, -1)
304
314
  ])])) : createCommentVNode("", true),
305
315
  __props.title ? (openBlock(), createElementBlock("span", _hoisted_14, toDisplayString(__props.title), 1)) : createCommentVNode("", true),
@@ -377,7 +387,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
377
387
  "stroke-width": "2",
378
388
  "stroke-linecap": "round",
379
389
  "stroke-linejoin": "round"
380
- }, [..._cache[14] || (_cache[14] = [
390
+ }, [..._cache[20] || (_cache[20] = [
381
391
  createElementVNode("path", { d: "m6 9 6 6 6-6" }, null, -1)
382
392
  ])], 2))
383
393
  ], 10, _hoisted_26)) : tab.href ? (openBlock(), createElementBlock("a", {
@@ -465,19 +475,34 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
465
475
  }), 128))
466
476
  ])) : createCommentVNode("", true),
467
477
  createElementVNode("div", _hoisted_38, [
468
- __props.showStandaloneLabel && isStandalone.value ? (openBlock(), createElementBlock("span", _hoisted_39, toDisplayString(__props.standaloneLabel), 1)) : createCommentVNode("", true),
469
- renderSlot(_ctx.$slots, "actions"),
470
- __props.showThemeToggle ? (openBlock(), createBlock(_sfc_main$1, {
478
+ __props.showStandaloneLabel && isStandalone.value && !unref(appExperiment) ? (openBlock(), createElementBlock("span", _hoisted_39, toDisplayString(__props.standaloneLabel), 1)) : createCommentVNode("", true),
479
+ unref(appExperiment) && !isStandalone.value ? (openBlock(), createBlock(_sfc_main$1, {
471
480
  key: 1,
481
+ "experiment-name": unref(appExperiment).experimentName.value,
482
+ "experiment-status": unref(appExperiment).experimentStatus.value,
483
+ "show-save": unref(appExperiment).showSave.value,
484
+ "show-detach": unref(appExperiment).showDetach.value,
485
+ "save-disabled": unref(appExperiment).saveDisabled.value,
486
+ "save-disabled-message": unref(appExperiment).saveDisabledMessage.value,
487
+ "save-loading": unref(appExperiment).saveLoading.value,
488
+ "save-success-message": unref(appExperiment).saveSuccessMessage.value,
489
+ onSelect: _cache[4] || (_cache[4] = ($event) => unref(appExperiment).openModal()),
490
+ onSave: _cache[5] || (_cache[5] = ($event) => unref(appExperiment).handleSave()),
491
+ onDetach: _cache[6] || (_cache[6] = ($event) => unref(appExperiment).handleDetach())
492
+ }, null, 8, ["experiment-name", "experiment-status", "show-save", "show-detach", "save-disabled", "save-disabled-message", "save-loading", "save-success-message"])) : createCommentVNode("", true),
493
+ unref(appExperiment) && isStandalone.value ? (openBlock(), createElementBlock("span", _hoisted_40, toDisplayString(__props.standaloneLabel), 1)) : createCommentVNode("", true),
494
+ renderSlot(_ctx.$slots, "actions"),
495
+ __props.showThemeToggle ? (openBlock(), createBlock(_sfc_main$2, {
496
+ key: 3,
472
497
  size: "sm"
473
498
  })) : createCommentVNode("", true),
474
499
  __props.showSettings ? (openBlock(), createElementBlock("button", {
475
- key: 2,
500
+ key: 4,
476
501
  type: "button",
477
502
  class: "mld-topbar__settings-btn",
478
503
  "aria-label": "Open settings",
479
- onClick: _cache[4] || (_cache[4] = ($event) => settingsOpen.value = true)
480
- }, [..._cache[15] || (_cache[15] = [
504
+ onClick: _cache[7] || (_cache[7] = ($event) => settingsOpen.value = true)
505
+ }, [..._cache[21] || (_cache[21] = [
481
506
  createElementVNode("svg", {
482
507
  class: "mld-topbar__settings-icon",
483
508
  viewBox: "0 0 24 24",
@@ -496,13 +521,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
496
521
  ], -1)
497
522
  ])])) : createCommentVNode("", true),
498
523
  __props.showAdmin ? (openBlock(), createBlock(_component_router_link, {
499
- key: 3,
524
+ key: 5,
500
525
  to: __props.adminPath,
501
526
  class: "mld-topbar__admin-btn",
502
527
  "aria-label": "Admin Dashboard",
503
- onClick: _cache[5] || (_cache[5] = ($event) => emit("admin-click"))
528
+ onClick: _cache[8] || (_cache[8] = ($event) => emit("admin-click"))
504
529
  }, {
505
- default: withCtx(() => [..._cache[16] || (_cache[16] = [
530
+ default: withCtx(() => [..._cache[22] || (_cache[22] = [
506
531
  createElementVNode("svg", {
507
532
  class: "mld-topbar__admin-icon",
508
533
  viewBox: "0 0 24 24",
@@ -519,22 +544,22 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
519
544
  _: 1
520
545
  }, 8, ["to"])) : createCommentVNode("", true),
521
546
  __props.showProfile ? (openBlock(), createElementBlock("button", {
522
- key: 4,
547
+ key: 6,
523
548
  type: "button",
524
549
  class: "mld-topbar__profile-btn",
525
550
  "aria-label": "Edit profile",
526
- onClick: _cache[6] || (_cache[6] = ($event) => emit("profile-click"))
551
+ onClick: _cache[9] || (_cache[9] = ($event) => emit("profile-click"))
527
552
  }, [
528
- createElementVNode("div", _hoisted_40, toDisplayString(profileInitial.value), 1),
529
- __props.userName ? (openBlock(), createElementBlock("span", _hoisted_41, toDisplayString(__props.userName), 1)) : createCommentVNode("", true)
553
+ createElementVNode("div", _hoisted_41, toDisplayString(profileInitial.value), 1),
554
+ __props.userName ? (openBlock(), createElementBlock("span", _hoisted_42, toDisplayString(__props.userName), 1)) : createCommentVNode("", true)
530
555
  ])) : createCommentVNode("", true)
531
556
  ])
532
557
  ])
533
558
  ], 2),
534
- __props.showSettings ? (openBlock(), createBlock(_sfc_main$2, {
559
+ __props.showSettings ? (openBlock(), createBlock(_sfc_main$3, {
535
560
  key: 0,
536
561
  modelValue: settingsOpen.value,
537
- "onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => settingsOpen.value = $event),
562
+ "onUpdate:modelValue": _cache[10] || (_cache[10] = ($event) => settingsOpen.value = $event),
538
563
  title: (_e = __props.settingsConfig) == null ? void 0 : _e.title,
539
564
  tabs: (_f = __props.settingsConfig) == null ? void 0 : _f.tabs,
540
565
  "show-appearance": ((_g = __props.settingsConfig) == null ? void 0 : _g.showAppearance) ?? true,
@@ -553,7 +578,15 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
553
578
  ])
554
579
  };
555
580
  })
556
- ]), 1032, ["modelValue", "title", "tabs", "show-appearance", "size"])) : createCommentVNode("", true)
581
+ ]), 1032, ["modelValue", "title", "tabs", "show-appearance", "size"])) : createCommentVNode("", true),
582
+ unref(appExperiment) && !isStandalone.value ? (openBlock(), createBlock(_sfc_main$4, {
583
+ key: 1,
584
+ "model-value": unref(appExperiment).showModal.value,
585
+ "current-experiment-id": unref(appExperiment).experimentId.value,
586
+ "onUpdate:modelValue": _cache[11] || (_cache[11] = ($event) => $event ? unref(appExperiment).openModal() : unref(appExperiment).closeModal()),
587
+ onSelect: _cache[12] || (_cache[12] = ($event) => unref(appExperiment).handleSelect($event)),
588
+ onDeselect: _cache[13] || (_cache[13] = ($event) => unref(appExperiment).handleDetach())
589
+ }, null, 8, ["model-value", "current-experiment-id"])) : createCommentVNode("", true)
557
590
  ], 64);
558
591
  };
559
592
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AppTopBar.vue.js","sources":["../../src/components/AppTopBar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, onMounted, onUnmounted } from 'vue'\nimport type { TopBarPage, TopBarTab, TopBarTabOption, TopBarSettingsConfig, TopBarVariant } from '../types/components'\nimport ThemeToggle from './ThemeToggle.vue'\nimport SettingsModal from './SettingsModal.vue'\nimport { usePlatformContext } from '../composables/usePlatformContext'\n\ninterface Props {\n title?: string\n subtitle?: string\n showLogo?: boolean\n variant?: TopBarVariant\n pluginName?: string\n pages?: TopBarPage[]\n currentPageId?: string\n tabs?: TopBarTab[]\n currentTabId?: string\n homePath?: string\n showThemeToggle?: boolean\n showSettings?: boolean\n settingsConfig?: TopBarSettingsConfig\n showStandaloneLabel?: boolean\n standaloneLabel?: string\n showAdmin?: boolean\n adminPath?: string\n showProfile?: boolean\n userName?: string\n userInitial?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showLogo: true,\n variant: 'card',\n homePath: '/',\n showThemeToggle: false,\n showSettings: false,\n showStandaloneLabel: true,\n standaloneLabel: 'Standalone',\n showAdmin: false,\n adminPath: '/admin',\n showProfile: false,\n})\n\nconst settingsOpen = ref(false)\nconst { isIntegrated } = usePlatformContext()\nconst isStandalone = computed(() => !isIntegrated.value)\n\nconst profileInitial = computed(() => {\n if (props.userInitial) return props.userInitial\n if (props.userName) return props.userName.charAt(0).toUpperCase()\n return 'U'\n})\n\nconst emit = defineEmits<{\n 'page-select': [page: TopBarPage]\n 'tab-select': [tab: TopBarTab]\n 'tab-option-select': [option: TopBarTabOption, tab: TopBarTab]\n 'profile-click': []\n 'admin-click': []\n}>()\n\nconst showPagesDropdown = ref(false)\nconst dropdownRef = ref<HTMLElement | null>(null)\nconst openTabDropdown = ref<string | null>(null)\nconst tabDropdownRefs = ref<Map<string, HTMLElement>>(new Map())\n\nfunction togglePagesDropdown() {\n showPagesDropdown.value = !showPagesDropdown.value\n openTabDropdown.value = null\n}\n\nfunction handlePageClick(page: TopBarPage) {\n if (page.disabled) return\n emit('page-select', page)\n showPagesDropdown.value = false\n}\n\nfunction toggleTabDropdown(tabId: string) {\n showPagesDropdown.value = false\n openTabDropdown.value = openTabDropdown.value === tabId ? null : tabId\n}\n\nfunction handleTabClick(tab: TopBarTab) {\n if (tab.disabled) return\n if (tab.children?.length) {\n toggleTabDropdown(tab.id)\n } else {\n emit('tab-select', tab)\n openTabDropdown.value = null\n }\n}\n\nfunction handleTabOptionClick(option: TopBarTabOption, tab: TopBarTab) {\n if (option.disabled) return\n emit('tab-option-select', option, tab)\n openTabDropdown.value = null\n}\n\nfunction setTabDropdownRef(el: HTMLElement | null, tabId: string) {\n if (el) {\n tabDropdownRefs.value.set(tabId, el)\n } else {\n tabDropdownRefs.value.delete(tabId)\n }\n}\n\nfunction handleClickOutside(event: MouseEvent) {\n const target = event.target as Node\n\n if (showPagesDropdown.value && dropdownRef.value && !dropdownRef.value.contains(target)) {\n showPagesDropdown.value = false\n }\n\n if (openTabDropdown.value !== null) {\n const clickedInside = Array.from(tabDropdownRefs.value.values()).some((el) => el.contains(target))\n if (!clickedInside) {\n openTabDropdown.value = null\n }\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', handleClickOutside)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', handleClickOutside)\n})\n</script>\n\n<template>\n <header\n :class=\"[\n 'mld-topbar',\n `mld-topbar--${props.variant}`,\n ]\"\n >\n <div class=\"mld-topbar__container\">\n <!-- Left section: Icon/Logo + Title/Subtitle or Breadcrumb -->\n <div class=\"mld-topbar__left\">\n <!-- Icon/Logo with external home link (absolute URL) -->\n <a v-if=\"homePath && homePath.startsWith('http')\" :href=\"homePath\" class=\"mld-topbar-home-link\">\n <slot name=\"icon\">\n <slot name=\"logo\">\n <div v-if=\"showLogo\" class=\"mld-topbar__logo\">\n <div class=\"mld-topbar__logo-icon\">\n <span class=\"mld-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </a>\n <!-- Icon/Logo with platform home link (absolute path goes to parent app) -->\n <a v-else-if=\"homePath && homePath.startsWith('/')\" :href=\"homePath\" class=\"mld-topbar-home-link\">\n <slot name=\"icon\">\n <slot name=\"logo\">\n <div v-if=\"showLogo\" class=\"mld-topbar__logo\">\n <div class=\"mld-topbar__logo-icon\">\n <span class=\"mld-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </a>\n <!-- Icon/Logo with router link (relative path within app) -->\n <router-link v-else-if=\"homePath\" :to=\"homePath\" class=\"mld-topbar-home-link\">\n <slot name=\"icon\">\n <slot name=\"logo\">\n <div v-if=\"showLogo\" class=\"mld-topbar__logo\">\n <div class=\"mld-topbar__logo-icon\">\n <span class=\"mld-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </router-link>\n <!-- Icon/Logo without link (homePath is empty) -->\n <template v-else>\n <slot name=\"icon\">\n <slot name=\"logo\">\n <div v-if=\"showLogo\" class=\"mld-topbar__logo\">\n <div class=\"mld-topbar__logo-icon\">\n <span class=\"mld-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </template>\n\n <!-- Title with subtitle (no breadcrumb) -->\n <div v-if=\"title && subtitle && !pluginName && !pages?.length\" class=\"mld-topbar-title-group\">\n <span class=\"mld-topbar-title\">{{ title }}</span>\n <span class=\"mld-topbar-subtitle\">{{ subtitle }}</span>\n </div>\n\n <!-- Breadcrumb: Plugin Name > Current Page -->\n <div v-else-if=\"pluginName || pages?.length\" ref=\"dropdownRef\" class=\"mld-topbar-breadcrumb\">\n <!-- Plugin name with dropdown trigger -->\n <button\n v-if=\"pages?.length\"\n type=\"button\"\n class=\"mld-topbar-plugin-name\"\n @click.stop=\"togglePagesDropdown\"\n >\n {{ pluginName }}\n <svg\n class=\"mld-topbar-chevron\"\n :class=\"{ 'mld-topbar-chevron--open': showPagesDropdown }\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n <span v-else class=\"mld-topbar-plugin-name--static\">{{ pluginName }}</span>\n\n <!-- Separator -->\n <svg\n v-if=\"title\"\n class=\"mld-topbar-separator\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n\n <!-- Current page title -->\n <span v-if=\"title\" class=\"mld-topbar-current-page\">{{ title }}</span>\n\n <!-- Pages dropdown -->\n <div v-show=\"showPagesDropdown\" class=\"mld-topbar-dropdown\">\n <template v-for=\"page in pages\" :key=\"page.id\">\n <a\n v-if=\"page.href\"\n :href=\"page.href\"\n :class=\"['mld-topbar-dropdown-item', { 'mld-topbar-dropdown-item--active': page.id === currentPageId, 'mld-topbar-dropdown-item--disabled': page.disabled }]\"\n @click=\"showPagesDropdown = false\"\n >\n <span class=\"mld-topbar-dropdown-item__label\">{{ page.label }}</span>\n <span v-if=\"page.description\" class=\"mld-topbar-dropdown-item__description\">{{ page.description }}</span>\n </a>\n <router-link\n v-else-if=\"page.to\"\n :to=\"page.to\"\n :class=\"['mld-topbar-dropdown-item', { 'mld-topbar-dropdown-item--active': page.id === currentPageId, 'mld-topbar-dropdown-item--disabled': page.disabled }]\"\n @click=\"showPagesDropdown = false\"\n >\n <span class=\"mld-topbar-dropdown-item__label\">{{ page.label }}</span>\n <span v-if=\"page.description\" class=\"mld-topbar-dropdown-item__description\">{{ page.description }}</span>\n </router-link>\n <button\n v-else\n type=\"button\"\n :class=\"['mld-topbar-dropdown-item', { 'mld-topbar-dropdown-item--active': page.id === currentPageId, 'mld-topbar-dropdown-item--disabled': page.disabled }]\"\n @click=\"handlePageClick(page)\"\n >\n <span class=\"mld-topbar-dropdown-item__label\">{{ page.label }}</span>\n <span v-if=\"page.description\" class=\"mld-topbar-dropdown-item__description\">{{ page.description }}</span>\n </button>\n </template>\n </div>\n </div>\n\n <!-- Title only (backward compat when no pluginName and no subtitle) -->\n <span v-else-if=\"title\" class=\"mld-topbar__title-only\">{{ title }}</span>\n\n <!-- Navigation slot -->\n <slot name=\"nav\" />\n </div>\n\n <!-- Center section: Tabs -->\n <div v-if=\"tabs?.length\" class=\"mld-topbar__tabs\">\n <template v-for=\"tab in tabs\" :key=\"tab.id\">\n <div\n :ref=\"(el) => tab.children?.length ? setTabDropdownRef(el as HTMLElement, tab.id) : null\"\n class=\"mld-topbar-tab-wrapper\"\n >\n <!-- Tab with children (dropdown) -->\n <button\n v-if=\"tab.children?.length\"\n type=\"button\"\n :class=\"[\n 'mld-topbar-tab',\n { 'mld-topbar-tab--active': tab.id === currentTabId || tab.children.some(c => c.id === currentTabId) },\n { 'mld-topbar-tab--disabled': tab.disabled }\n ]\"\n @click.stop=\"handleTabClick(tab)\"\n >\n {{ tab.label }}\n <svg\n class=\"mld-topbar-tab-chevron\"\n :class=\"{ 'mld-topbar-tab-chevron--open': openTabDropdown === tab.id }\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <!-- Simple tab (no children) -->\n <a\n v-else-if=\"tab.href\"\n :href=\"tab.href\"\n :class=\"[\n 'mld-topbar-tab',\n { 'mld-topbar-tab--active': tab.id === currentTabId },\n { 'mld-topbar-tab--disabled': tab.disabled }\n ]\"\n >\n {{ tab.label }}\n </a>\n <router-link\n v-else-if=\"tab.to\"\n :to=\"tab.to\"\n :class=\"[\n 'mld-topbar-tab',\n { 'mld-topbar-tab--active': tab.id === currentTabId },\n { 'mld-topbar-tab--disabled': tab.disabled }\n ]\"\n >\n {{ tab.label }}\n </router-link>\n <button\n v-else\n type=\"button\"\n :class=\"[\n 'mld-topbar-tab',\n { 'mld-topbar-tab--active': tab.id === currentTabId },\n { 'mld-topbar-tab--disabled': tab.disabled }\n ]\"\n @click=\"handleTabClick(tab)\"\n >\n {{ tab.label }}\n </button>\n\n <!-- Tab dropdown -->\n <div v-if=\"tab.children?.length\" v-show=\"openTabDropdown === tab.id\" class=\"mld-topbar-tab-dropdown\">\n <template v-for=\"option in tab.children\" :key=\"option.id\">\n <a\n v-if=\"option.href\"\n :href=\"option.href\"\n :class=\"[\n 'mld-topbar-dropdown-item',\n { 'mld-topbar-dropdown-item--active': option.id === currentTabId },\n { 'mld-topbar-dropdown-item--disabled': option.disabled }\n ]\"\n @click=\"openTabDropdown = null\"\n >\n <span class=\"mld-topbar-dropdown-item__label\">{{ option.label }}</span>\n <span v-if=\"option.description\" class=\"mld-topbar-dropdown-item__description\">{{ option.description }}</span>\n </a>\n <router-link\n v-else-if=\"option.to\"\n :to=\"option.to\"\n :class=\"[\n 'mld-topbar-dropdown-item',\n { 'mld-topbar-dropdown-item--active': option.id === currentTabId },\n { 'mld-topbar-dropdown-item--disabled': option.disabled }\n ]\"\n @click=\"openTabDropdown = null\"\n >\n <span class=\"mld-topbar-dropdown-item__label\">{{ option.label }}</span>\n <span v-if=\"option.description\" class=\"mld-topbar-dropdown-item__description\">{{ option.description }}</span>\n </router-link>\n <button\n v-else\n type=\"button\"\n :class=\"[\n 'mld-topbar-dropdown-item',\n { 'mld-topbar-dropdown-item--active': option.id === currentTabId },\n { 'mld-topbar-dropdown-item--disabled': option.disabled }\n ]\"\n @click=\"handleTabOptionClick(option, tab)\"\n >\n <span class=\"mld-topbar-dropdown-item__label\">{{ option.label }}</span>\n <span v-if=\"option.description\" class=\"mld-topbar-dropdown-item__description\">{{ option.description }}</span>\n </button>\n </template>\n </div>\n </div>\n </template>\n </div>\n\n <!-- Right section: Actions -->\n <div class=\"mld-topbar__right\">\n <span v-if=\"showStandaloneLabel && isStandalone\" class=\"mld-topbar__standalone-badge\">\n {{ standaloneLabel }}\n </span>\n <!-- Actions slot (right side) -->\n <slot name=\"actions\" />\n <!-- Theme toggle -->\n <ThemeToggle v-if=\"showThemeToggle\" size=\"sm\" />\n <!-- Settings gear -->\n <button\n v-if=\"showSettings\"\n type=\"button\"\n class=\"mld-topbar__settings-btn\"\n aria-label=\"Open settings\"\n @click=\"settingsOpen = true\"\n >\n <svg class=\"mld-topbar__settings-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915\" /><circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n </button>\n <!-- Admin link -->\n <router-link\n v-if=\"showAdmin\"\n :to=\"adminPath\"\n class=\"mld-topbar__admin-btn\"\n aria-label=\"Admin Dashboard\"\n @click=\"emit('admin-click')\"\n >\n <svg class=\"mld-topbar__admin-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z\" /><path d=\"m9 12 2 2 4-4\" />\n </svg>\n </router-link>\n <!-- Profile button -->\n <button\n v-if=\"showProfile\"\n type=\"button\"\n class=\"mld-topbar__profile-btn\"\n aria-label=\"Edit profile\"\n @click=\"emit('profile-click')\"\n >\n <div class=\"mld-topbar__profile-avatar\">\n {{ profileInitial }}\n </div>\n <span v-if=\"userName\" class=\"mld-topbar__profile-name\">{{ userName }}</span>\n </button>\n </div>\n </div>\n </header>\n\n <SettingsModal\n v-if=\"showSettings\"\n v-model=\"settingsOpen\"\n :title=\"settingsConfig?.title\"\n :tabs=\"settingsConfig?.tabs\"\n :show-appearance=\"settingsConfig?.showAppearance ?? true\"\n :size=\"settingsConfig?.size\"\n >\n <template v-for=\"tab in (settingsConfig?.tabs ?? [])\" :key=\"tab.id\" #[`tab-${tab.id}`]>\n <slot :name=\"`settings-tab-${tab.id}`\" />\n </template>\n <template #appearance>\n <slot name=\"settings-appearance\" />\n </template>\n </SettingsModal>\n</template>\n\n<style>\n@import '../styles/components/app-top-bar.css';\n</style>\n"],"names":["_createElementVNode","_normalizeClass","_createElementBlock","_renderSlot","_openBlock","_createBlock","_toDisplayString","_createTextVNode","_withDirectives","_Fragment","_renderList","_a","_withModifiers","_b","ThemeToggle","SettingsModal"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,UAAM,QAAQ;AAad,UAAM,eAAe,IAAI,KAAK;AAC9B,UAAM,EAAE,aAAA,IAAiB,mBAAA;AACzB,UAAM,eAAe,SAAS,MAAM,CAAC,aAAa,KAAK;AAEvD,UAAM,iBAAiB,SAAS,MAAM;AACpC,UAAI,MAAM,YAAa,QAAO,MAAM;AACpC,UAAI,MAAM,SAAU,QAAO,MAAM,SAAS,OAAO,CAAC,EAAE,YAAA;AACpD,aAAO;AAAA,IACT,CAAC;AAED,UAAM,OAAO;AAQb,UAAM,oBAAoB,IAAI,KAAK;AACnC,UAAM,cAAc,IAAwB,IAAI;AAChD,UAAM,kBAAkB,IAAmB,IAAI;AAC/C,UAAM,kBAAkB,IAA8B,oBAAI,KAAK;AAE/D,aAAS,sBAAsB;AAC7B,wBAAkB,QAAQ,CAAC,kBAAkB;AAC7C,sBAAgB,QAAQ;AAAA,IAC1B;AAEA,aAAS,gBAAgB,MAAkB;AACzC,UAAI,KAAK,SAAU;AACnB,WAAK,eAAe,IAAI;AACxB,wBAAkB,QAAQ;AAAA,IAC5B;AAEA,aAAS,kBAAkB,OAAe;AACxC,wBAAkB,QAAQ;AAC1B,sBAAgB,QAAQ,gBAAgB,UAAU,QAAQ,OAAO;AAAA,IACnE;AAEA,aAAS,eAAe,KAAgB;;AACtC,UAAI,IAAI,SAAU;AAClB,WAAI,SAAI,aAAJ,mBAAc,QAAQ;AACxB,0BAAkB,IAAI,EAAE;AAAA,MAC1B,OAAO;AACL,aAAK,cAAc,GAAG;AACtB,wBAAgB,QAAQ;AAAA,MAC1B;AAAA,IACF;AAEA,aAAS,qBAAqB,QAAyB,KAAgB;AACrE,UAAI,OAAO,SAAU;AACrB,WAAK,qBAAqB,QAAQ,GAAG;AACrC,sBAAgB,QAAQ;AAAA,IAC1B;AAEA,aAAS,kBAAkB,IAAwB,OAAe;AAChE,UAAI,IAAI;AACN,wBAAgB,MAAM,IAAI,OAAO,EAAE;AAAA,MACrC,OAAO;AACL,wBAAgB,MAAM,OAAO,KAAK;AAAA,MACpC;AAAA,IACF;AAEA,aAAS,mBAAmB,OAAmB;AAC7C,YAAM,SAAS,MAAM;AAErB,UAAI,kBAAkB,SAAS,YAAY,SAAS,CAAC,YAAY,MAAM,SAAS,MAAM,GAAG;AACvF,0BAAkB,QAAQ;AAAA,MAC5B;AAEA,UAAI,gBAAgB,UAAU,MAAM;AAClC,cAAM,gBAAgB,MAAM,KAAK,gBAAgB,MAAM,OAAA,CAAQ,EAAE,KAAK,CAAC,OAAO,GAAG,SAAS,MAAM,CAAC;AACjG,YAAI,CAAC,eAAe;AAClB,0BAAgB,QAAQ;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AAEA,cAAU,MAAM;AACd,eAAS,iBAAiB,SAAS,kBAAkB;AAAA,IACvD,CAAC;AAED,gBAAY,MAAM;AAChB,eAAS,oBAAoB,SAAS,kBAAkB;AAAA,IAC1D,CAAC;;;;;QAICA,mBA8TS,UAAA;AAAA,UA7TN,OAAKC,eAAA;AAAA;YAA6C,eAAA,MAAM,OAAO;AAAA,UAAA;;UAKhED,mBAuTM,OAvTN,YAuTM;AAAA,YArTJA,mBA6IM,OA7IN,YA6IM;AAAA,cA3IK,QAAA,YAAY,QAAA,SAAS,WAAU,MAAA,kBAAxCE,mBAUI,KAAA;AAAA;gBAV+C,MAAM,QAAA;AAAA,gBAAU,OAAM;AAAA,cAAA;gBACvEC,WAQO,yBARP,MAQO;AAAA,kBAPLA,WAMO,yBANP,MAMO;AAAA,oBALM,QAAA,YAAXC,aAAAF,mBAIM,OAJN,YAIM,CAAA,GAAA,OAAA,CAAA,MAAA,OAAA,CAAA,IAAA;AAAA,sBAHJF,mBAEM,OAAA,EAFD,OAAM,2BAAuB;AAAA,wBAChCA,mBAA4C,QAAA,EAAtC,OAAM,wBAAA,GAAwB,GAAC;AAAA,sBAAA;;;;mCAOjC,QAAA,YAAY,QAAA,SAAS,WAAU,GAAA,kBAA7CE,mBAUI,KAAA;AAAA;gBAViD,MAAM,QAAA;AAAA,gBAAU,OAAM;AAAA,cAAA;gBACzEC,WAQO,yBARP,MAQO;AAAA,kBAPLA,WAMO,yBANP,MAMO;AAAA,oBALM,QAAA,YAAXC,aAAAF,mBAIM,OAJN,YAIM,CAAA,GAAA,OAAA,CAAA,MAAA,OAAA,CAAA,IAAA;AAAA,sBAHJF,mBAEM,OAAA,EAFD,OAAM,2BAAuB;AAAA,wBAChCA,mBAA4C,QAAA,EAAtC,OAAM,wBAAA,GAAwB,GAAC;AAAA,sBAAA;;;;mCAOvB,QAAA,yBAAxBK,YAUc,wBAAA;AAAA;gBAVqB,IAAI,QAAA;AAAA,gBAAU,OAAM;AAAA,cAAA;iCACrD,MAQO;AAAA,kBARPF,WAQO,yBARP,MAQO;AAAA,oBAPLA,WAMO,yBANP,MAMO;AAAA,sBALM,QAAA,YAAXC,aAAAF,mBAIM,OAJN,YAIM,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,wBAHJF,mBAEM,OAAA,EAFD,OAAM,2BAAuB;AAAA,0BAChCA,mBAA4C,QAAA,EAAtC,OAAM,wBAAA,GAAwB,GAAC;AAAA,wBAAA;;;;;;+BAQ7CG,WAQO,iCARP,MAQO;AAAA,gBAPLA,WAMO,yBANP,MAMO;AAAA,kBALM,QAAA,YAAXC,aAAAF,mBAIM,OAJN,YAIM,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,oBAHJF,mBAEM,OAAA,EAFD,OAAM,2BAAuB;AAAA,sBAChCA,mBAA4C,QAAA,EAAtC,OAAM,wBAAA,GAAwB,GAAC;AAAA,oBAAA;;;;cAQpC,QAAA,SAAS,QAAA,YAAQ,CAAK,sBAAU,GAAK,aAAA,UAAA,mBAAO,WAAvDI,UAAA,GAAAF,mBAGM,OAHN,YAGM;AAAA,gBAFJF,mBAAiD,QAAjD,aAAiDM,gBAAf,QAAA,KAAK,GAAA,CAAA;AAAA,gBACvCN,mBAAuD,QAAvD,aAAuDM,gBAAlB,QAAA,QAAQ,GAAA,CAAA;AAAA,cAAA,MAI/B,QAAA,gBAAc,aAAA,UAAA,mBAAO,wBAArCJ,mBA6EM,OAAA;AAAA;yBA7E2C;AAAA,gBAAJ,KAAI;AAAA,gBAAc,OAAM;AAAA,cAAA;kBAG3D,aAAA,UAAA,mBAAO,wBADfA,mBAqBS,UAAA;AAAA;kBAnBP,MAAK;AAAA,kBACL,OAAM;AAAA,kBACL,uBAAY,qBAAmB,CAAA,MAAA,CAAA;AAAA,gBAAA;kBAE7BK,gBAAAD,gBAAA,QAAA,UAAU,IAAG,KAChB,CAAA;AAAA,gCAAAJ,mBAaM,OAAA;AAAA,oBAZJ,OAAKD,eAAA,CAAC,sBAAoB,EAAA,4BACY,kBAAA,MAAA,CAAiB,CAAA;AAAA,oBACvD,OAAM;AAAA,oBACN,QAAO;AAAA,oBACP,SAAQ;AAAA,oBACR,MAAK;AAAA,oBACL,QAAO;AAAA,oBACP,gBAAa;AAAA,oBACb,kBAAe;AAAA,oBACf,mBAAgB;AAAA,kBAAA;oBAEhBD,mBAAyB,QAAA,EAAnB,GAAE,eAAA,GAAc,MAAA,EAAA;AAAA,kBAAA;oCAG1BE,mBAA2E,QAA3E,aAA2EI,gBAApB,QAAA,UAAU,GAAA,CAAA;AAAA,gBAIzD,QAAA,SADRF,aAAAF,mBAaM,OAbN,aAaM,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,kBADJF,mBAA0B,QAAA,EAApB,GAAE,gBAAA,GAAe,MAAA,EAAA;AAAA,gBAAA;gBAIb,QAAA,sBAAZE,mBAAqE,QAArE,aAAqEI,gBAAf,QAAA,KAAK,GAAA,CAAA;gBAG3DE,eAAAR,mBA8BM,OA9BN,aA8BM;AAAA,oCA7BJE,mBA4BWO,UAAA,MAAAC,WA5Bc,QAAA,OAAK,CAAb,SAAI;;sBAAiB,KAAA,KAAK;AAAA,oBAAA;sBAEjC,KAAK,qBADbR,mBAQI,KAAA;AAAA;wBAND,MAAM,KAAK;AAAA,wBACX,OAAKD,eAAA,CAAA,4BAAA,EAAA,oCAAqE,KAAK,OAAO,QAAA,eAAa,sCAAwC,KAAK,SAAA,CAAQ,CAAA;AAAA,wBACxJ,+CAAO,kBAAA,QAAiB;AAAA,sBAAA;wBAEzBD,mBAAqE,QAArE,aAAqEM,gBAApB,KAAK,KAAK,GAAA,CAAA;AAAA,wBAC/C,KAAK,eAAjBF,UAAA,GAAAF,mBAAyG,QAAzG,aAAyGI,gBAA1B,KAAK,WAAW,GAAA,CAAA;6CAGpF,KAAK,mBADlBD,YAQc,wBAAA;AAAA;wBANX,IAAI,KAAK;AAAA,wBACT,OAAKJ,eAAA,CAAA,4BAAA,EAAA,oCAAqE,KAAK,OAAO,QAAA,eAAa,sCAAwC,KAAK,SAAA,CAAQ,CAAA;AAAA,wBACxJ,+CAAO,kBAAA,QAAiB;AAAA,sBAAA;yCAEzB,MAAqE;AAAA,0BAArED,mBAAqE,QAArE,aAAqEM,gBAApB,KAAK,KAAK,GAAA,CAAA;AAAA,0BAC/C,KAAK,eAAjBF,UAAA,GAAAF,mBAAyG,QAAzG,aAAyGI,gBAA1B,KAAK,WAAW,GAAA,CAAA;;;iEAEjGJ,mBAQS,UAAA;AAAA;wBANP,MAAK;AAAA,wBACJ,OAAKD,eAAA,CAAA,4BAAA,EAAA,oCAAqE,KAAK,OAAO,QAAA,eAAa,sCAAwC,KAAK,SAAA,CAAQ,CAAA;AAAA,wBACxJ,SAAK,CAAA,WAAE,gBAAgB,IAAI;AAAA,sBAAA;wBAE5BD,mBAAqE,QAArE,aAAqEM,gBAApB,KAAK,KAAK,GAAA,CAAA;AAAA,wBAC/C,KAAK,eAAjBF,UAAA,GAAAF,mBAAyG,QAAzG,aAAyGI,gBAA1B,KAAK,WAAW,GAAA,CAAA;;;;;0BA3BxF,kBAAA,KAAiB;AAAA,gBAAA;yBAkCf,QAAA,sBAAjBJ,mBAAyE,QAAzE,aAAyEI,gBAAf,QAAA,KAAK,GAAA,CAAA;cAG/DH,WAAmB,KAAA,QAAA,KAAA;AAAA,YAAA;cAIV,aAAA,SAAA,mBAAM,WAAjBC,aAAAF,mBAoHM,OApHN,aAoHM;AAAA,gCAnHJA,mBAkHWO,UAAA,MAAAC,WAlHa,QAAA,MAAI,CAAX,QAAG;;oCAClBR,mBAgHM,OAAA;AAAA,kBAjH4B,KAAA,IAAI;AAAA;kBAEnC,KAAG,CAAG;;AAAO,6BAAAS,MAAA,IAAI,aAAJ,gBAAAA,IAAc,UAAS,kBAAkB,IAAmB,IAAI,EAAE,IAAA;AAAA;AAAA,kBAChF,OAAM;AAAA,gBAAA;oBAIEA,MAAA,IAAI,aAAJ,gBAAAA,IAAc,wBADtBT,mBAyBS,UAAA;AAAA;oBAvBP,MAAK;AAAA,oBACJ,OAAKD,eAAA;AAAA;sBAAkF,EAAA,0BAAA,IAAI,OAAO,QAAA,gBAAgB,IAAI,SAAS,KAAK,CAAA,MAAK,EAAE,OAAO,QAAA,YAAY,EAAA;AAAA,sBAAmD,EAAA,4BAAA,IAAI,SAAA;AAAA,oBAAQ;oBAK7N,SAAKW,cAAA,CAAA,WAAO,eAAe,GAAG,GAAA,CAAA,MAAA,CAAA;AAAA,kBAAA;oDAE5B,IAAI,KAAK,IAAG,KACf,CAAA;AAAA,kCAAAV,mBAaM,OAAA;AAAA,sBAZJ,uBAAM,0BAAwB,EAAA,gCACY,0BAAoB,IAAI,GAAA,CAAE,CAAA;AAAA,sBACpE,OAAM;AAAA,sBACN,QAAO;AAAA,sBACP,SAAQ;AAAA,sBACR,MAAK;AAAA,sBACL,QAAO;AAAA,sBACP,gBAAa;AAAA,sBACb,kBAAe;AAAA,sBACf,mBAAgB;AAAA,oBAAA;sBAEhBF,mBAAyB,QAAA,EAAnB,GAAE,eAAA,GAAc,MAAA,EAAA;AAAA,oBAAA;yCAMb,IAAI,qBADjBE,mBAUI,KAAA;AAAA;oBARD,MAAM,IAAI;AAAA,oBACV,OAAKD,eAAA;AAAA;kDAAkF,IAAI,OAAO,QAAA,aAAA;AAAA,sBAA8D,EAAA,4BAAA,IAAI,SAAA;AAAA,oBAAQ;qBAM1KK,gBAAA,IAAI,KAAK,GAAA,IAAA,WAAA,KAGD,IAAI,mBADjBD,YAUc,wBAAA;AAAA;oBARX,IAAI,IAAI;AAAA,oBACR,OAAKJ,eAAA;AAAA;kDAAkF,IAAI,OAAO,QAAA,aAAA;AAAA,sBAA8D,EAAA,4BAAA,IAAI,SAAA;AAAA,oBAAQ;;qCAM7K,MAAe;AAAA,sBAAZM,gBAAAD,gBAAA,IAAI,KAAK,GAAA,CAAA;AAAA,oBAAA;;6DAEdJ,mBAWS,UAAA;AAAA;oBATP,MAAK;AAAA,oBACJ,OAAKD,eAAA;AAAA;kDAAkF,IAAI,OAAO,QAAA,aAAA;AAAA,sBAA8D,EAAA,4BAAA,IAAI,SAAA;AAAA,oBAAQ;oBAK5K,SAAK,CAAA,WAAE,eAAe,GAAG;AAAA,kBAAA,GAEvBK,gBAAA,IAAI,KAAK,GAAA,IAAA,WAAA;AAAA,oBAIHO,MAAA,IAAI,aAAJ,gBAAAA,IAAc,UAAzBL,gBAAAJ,aAAAF,mBA0CM,OA1CN,aA0CM;AAAA,qBAzCJE,UAAA,IAAA,GAAAF,mBAwCWO,UAAA,MAAAC,WAxCgB,IAAI,WAAd,WAAM;;wBAAwB,KAAA,OAAO;AAAA,sBAAA;wBAE5C,OAAO,qBADfR,mBAYI,KAAA;AAAA;0BAVD,MAAM,OAAO;AAAA,0BACb,OAAKD,eAAA;AAAA;kEAA8G,OAAO,OAAO,QAAA,aAAA;AAAA,4BAA4E,EAAA,sCAAA,OAAO,SAAA;AAAA,0BAAQ;0BAK5N,+CAAO,gBAAA,QAAe;AAAA,wBAAA;0BAEvBD,mBAAuE,QAAvE,aAAuEM,gBAAtB,OAAO,KAAK,GAAA,CAAA;AAAA,0BACjD,OAAO,eAAnBF,UAAA,GAAAF,mBAA6G,QAA7G,aAA6GI,gBAA5B,OAAO,WAAW,GAAA,CAAA;+CAGxF,OAAO,mBADpBD,YAYc,wBAAA;AAAA;0BAVX,IAAI,OAAO;AAAA,0BACX,OAAKJ,eAAA;AAAA;kEAA8G,OAAO,OAAO,QAAA,aAAA;AAAA,4BAA4E,EAAA,sCAAA,OAAO,SAAA;AAAA,0BAAQ;0BAK5N,+CAAO,gBAAA,QAAe;AAAA,wBAAA;2CAEvB,MAAuE;AAAA,4BAAvED,mBAAuE,QAAvE,aAAuEM,gBAAtB,OAAO,KAAK,GAAA,CAAA;AAAA,4BACjD,OAAO,eAAnBF,UAAA,GAAAF,mBAA6G,QAA7G,aAA6GI,gBAA5B,OAAO,WAAW,GAAA,CAAA;;;mEAErGJ,mBAYS,UAAA;AAAA;0BAVP,MAAK;AAAA,0BACJ,OAAKD,eAAA;AAAA;kEAA8G,OAAO,OAAO,QAAA,aAAA;AAAA,4BAA4E,EAAA,sCAAA,OAAO,SAAA;AAAA,0BAAQ;0BAK5N,SAAK,CAAA,WAAE,qBAAqB,QAAQ,GAAG;AAAA,wBAAA;0BAExCD,mBAAuE,QAAvE,aAAuEM,gBAAtB,OAAO,KAAK,GAAA,CAAA;AAAA,0BACjD,OAAO,eAAnBF,UAAA,GAAAF,mBAA6G,QAA7G,aAA6GI,gBAA5B,OAAO,WAAW,GAAA,CAAA;;;;;4BAvChE,gBAAA,UAAoB,IAAI,EAAE;AAAA,kBAAA;;;;YAgDzEN,mBA6CM,OA7CN,aA6CM;AAAA,cA5CQ,QAAA,uBAAuB,aAAA,sBAAnCE,mBAEO,QAFP,aAEOI,gBADF,QAAA,eAAe,GAAA,CAAA;cAGpBH,WAAuB,KAAA,QAAA,SAAA;AAAA,cAEJ,QAAA,gCAAnBE,YAAgDS,aAAA;AAAA;gBAAZ,MAAK;AAAA,cAAA;cAGjC,QAAA,6BADRZ,mBAUS,UAAA;AAAA;gBARP,MAAK;AAAA,gBACL,OAAM;AAAA,gBACN,cAAW;AAAA,gBACV,+CAAO,aAAA,QAAY;AAAA,cAAA;gBAEpBF,mBAEM,OAAA;AAAA,kBAFD,OAAM;AAAA,kBAA4B,SAAQ;AAAA,kBAAY,MAAK;AAAA,kBAAO,QAAO;AAAA,kBAAe,gBAAa;AAAA,kBAAI,kBAAe;AAAA,kBAAQ,mBAAgB;AAAA,gBAAA;kBACnJA,mBAAoV,QAAA,EAA9U,GAAE,2UAAyU;AAAA,kBAAGA,mBAAgC,UAAA;AAAA,oBAAxB,IAAG;AAAA,oBAAK,IAAG;AAAA,oBAAK,GAAE;AAAA,kBAAA;;;cAK1W,QAAA,0BADRK,YAUc,wBAAA;AAAA;gBARX,IAAI,QAAA;AAAA,gBACL,OAAM;AAAA,gBACN,cAAW;AAAA,gBACV,+CAAO,KAAI,aAAA;AAAA,cAAA;iCAEZ,MAEM,CAAA,GAAA,OAAA,EAAA,MAAA,OAAA,EAAA,IAAA;AAAA,kBAFNL,mBAEM,OAAA;AAAA,oBAFD,OAAM;AAAA,oBAAyB,SAAQ;AAAA,oBAAY,MAAK;AAAA,oBAAO,QAAO;AAAA,oBAAe,gBAAa;AAAA,oBAAI,kBAAe;AAAA,oBAAQ,mBAAgB;AAAA,kBAAA;oBAChJA,mBAA+K,QAAA,EAAzK,GAAE,sKAAoK;AAAA,oBAAGA,mBAA0B,QAAA,EAApB,GAAE,iBAAe;AAAA,kBAAA;;;;cAKlM,QAAA,4BADRE,mBAWS,UAAA;AAAA;gBATP,MAAK;AAAA,gBACL,OAAM;AAAA,gBACN,cAAW;AAAA,gBACV,+CAAO,KAAI,eAAA;AAAA,cAAA;gBAEZF,mBAEM,OAFN,aAEMM,gBADD,eAAA,KAAc,GAAA,CAAA;AAAA,gBAEP,QAAA,yBAAZJ,mBAA4E,QAA5E,aAA4EI,gBAAlB,QAAA,QAAQ,GAAA,CAAA;;;;;QAOlE,QAAA,6BADRD,YAcgBU,aAAA;AAAA;sBAZL,aAAA;AAAA,uEAAA,aAAY,QAAA;AAAA,UACpB,QAAO,aAAA,mBAAA,mBAAgB;AAAA,UACvB,OAAM,aAAA,mBAAA,mBAAgB;AAAA,UACtB,qBAAiB,aAAA,mBAAA,mBAAgB,mBAAc;AAAA,UAC/C,OAAM,aAAA,mBAAA,mBAAgB;AAAA,QAAA;UAKZ,oBACT,MAAmC;AAAA,YAAnCZ,WAAmC,KAAA,QAAA,qBAAA;AAAA,UAAA;;;uBAJZ,aAAA,mBAAA,mBAAgB,SAAI,CAAA,GAAA,CAA5B,QAAG;;cAAwD,MAAA,OAAA,IAAI,EAAE;AAAA,0BAChF,MAAyC;AAAA,gBAAzCA,WAAyC,KAAA,QAAA,gBAAZ,IAAI,EAAE,EAAA;AAAA,cAAA;;;;;;;;"}
1
+ {"version":3,"file":"AppTopBar.vue.js","sources":["../../src/components/AppTopBar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, inject, onMounted, onUnmounted } from 'vue'\nimport type { TopBarPage, TopBarTab, TopBarTabOption, TopBarSettingsConfig, TopBarVariant } from '../types/components'\nimport ThemeToggle from './ThemeToggle.vue'\nimport SettingsModal from './SettingsModal.vue'\nimport ExperimentPopover from './ExperimentPopover.vue'\nimport ExperimentSelectorModal from './ExperimentSelectorModal.vue'\nimport { usePlatformContext } from '../composables/usePlatformContext'\nimport { APP_EXPERIMENT_KEY } from '../composables/useAppExperiment'\n\ninterface Props {\n title?: string\n subtitle?: string\n showLogo?: boolean\n variant?: TopBarVariant\n pluginName?: string\n pages?: TopBarPage[]\n currentPageId?: string\n tabs?: TopBarTab[]\n currentTabId?: string\n homePath?: string\n showThemeToggle?: boolean\n showSettings?: boolean\n settingsConfig?: TopBarSettingsConfig\n showStandaloneLabel?: boolean\n standaloneLabel?: string\n showAdmin?: boolean\n adminPath?: string\n showProfile?: boolean\n userName?: string\n userInitial?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showLogo: true,\n variant: 'card',\n homePath: '/',\n showThemeToggle: false,\n showSettings: false,\n showStandaloneLabel: true,\n standaloneLabel: 'Standalone',\n showAdmin: false,\n adminPath: '/admin',\n showProfile: false,\n})\n\nconst settingsOpen = ref(false)\nconst { isIntegrated } = usePlatformContext()\nconst isStandalone = computed(() => !isIntegrated.value)\nconst appExperiment = inject(APP_EXPERIMENT_KEY, null)\n\nconst profileInitial = computed(() => {\n if (props.userInitial) return props.userInitial\n if (props.userName) return props.userName.charAt(0).toUpperCase()\n return 'U'\n})\n\nconst emit = defineEmits<{\n 'page-select': [page: TopBarPage]\n 'tab-select': [tab: TopBarTab]\n 'tab-option-select': [option: TopBarTabOption, tab: TopBarTab]\n 'profile-click': []\n 'admin-click': []\n}>()\n\nconst showPagesDropdown = ref(false)\nconst dropdownRef = ref<HTMLElement | null>(null)\nconst openTabDropdown = ref<string | null>(null)\nconst tabDropdownRefs = ref<Map<string, HTMLElement>>(new Map())\n\nfunction togglePagesDropdown() {\n showPagesDropdown.value = !showPagesDropdown.value\n openTabDropdown.value = null\n}\n\nfunction handlePageClick(page: TopBarPage) {\n if (page.disabled) return\n emit('page-select', page)\n showPagesDropdown.value = false\n}\n\nfunction toggleTabDropdown(tabId: string) {\n showPagesDropdown.value = false\n openTabDropdown.value = openTabDropdown.value === tabId ? null : tabId\n}\n\nfunction handleTabClick(tab: TopBarTab) {\n if (tab.disabled) return\n if (tab.children?.length) {\n toggleTabDropdown(tab.id)\n } else {\n emit('tab-select', tab)\n openTabDropdown.value = null\n }\n}\n\nfunction handleTabOptionClick(option: TopBarTabOption, tab: TopBarTab) {\n if (option.disabled) return\n emit('tab-option-select', option, tab)\n openTabDropdown.value = null\n}\n\nfunction setTabDropdownRef(el: HTMLElement | null, tabId: string) {\n if (el) {\n tabDropdownRefs.value.set(tabId, el)\n } else {\n tabDropdownRefs.value.delete(tabId)\n }\n}\n\nfunction handleClickOutside(event: MouseEvent) {\n const target = event.target as Node\n\n if (showPagesDropdown.value && dropdownRef.value && !dropdownRef.value.contains(target)) {\n showPagesDropdown.value = false\n }\n\n if (openTabDropdown.value !== null) {\n const clickedInside = Array.from(tabDropdownRefs.value.values()).some((el) => el.contains(target))\n if (!clickedInside) {\n openTabDropdown.value = null\n }\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', handleClickOutside)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', handleClickOutside)\n})\n</script>\n\n<template>\n <header\n :class=\"[\n 'mld-topbar',\n `mld-topbar--${props.variant}`,\n ]\"\n >\n <div class=\"mld-topbar__container\">\n <!-- Left section: Icon/Logo + Title/Subtitle or Breadcrumb -->\n <div class=\"mld-topbar__left\">\n <!-- Icon/Logo with external home link (absolute URL) -->\n <a v-if=\"homePath && homePath.startsWith('http')\" :href=\"homePath\" class=\"mld-topbar-home-link\">\n <slot name=\"icon\">\n <slot name=\"logo\">\n <div v-if=\"showLogo\" class=\"mld-topbar__logo\">\n <div class=\"mld-topbar__logo-icon\">\n <span class=\"mld-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </a>\n <!-- Icon/Logo with platform home link (absolute path goes to parent app) -->\n <a v-else-if=\"homePath && homePath.startsWith('/')\" :href=\"homePath\" class=\"mld-topbar-home-link\">\n <slot name=\"icon\">\n <slot name=\"logo\">\n <div v-if=\"showLogo\" class=\"mld-topbar__logo\">\n <div class=\"mld-topbar__logo-icon\">\n <span class=\"mld-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </a>\n <!-- Icon/Logo with router link (relative path within app) -->\n <router-link v-else-if=\"homePath\" :to=\"homePath\" class=\"mld-topbar-home-link\">\n <slot name=\"icon\">\n <slot name=\"logo\">\n <div v-if=\"showLogo\" class=\"mld-topbar__logo\">\n <div class=\"mld-topbar__logo-icon\">\n <span class=\"mld-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </router-link>\n <!-- Icon/Logo without link (homePath is empty) -->\n <template v-else>\n <slot name=\"icon\">\n <slot name=\"logo\">\n <div v-if=\"showLogo\" class=\"mld-topbar__logo\">\n <div class=\"mld-topbar__logo-icon\">\n <span class=\"mld-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </template>\n\n <!-- Title with subtitle (no breadcrumb) -->\n <div v-if=\"title && subtitle && !pluginName && !pages?.length\" class=\"mld-topbar-title-group\">\n <span class=\"mld-topbar-title\">{{ title }}</span>\n <span class=\"mld-topbar-subtitle\">{{ subtitle }}</span>\n </div>\n\n <!-- Breadcrumb: Plugin Name > Current Page -->\n <div v-else-if=\"pluginName || pages?.length\" ref=\"dropdownRef\" class=\"mld-topbar-breadcrumb\">\n <!-- Plugin name with dropdown trigger -->\n <button\n v-if=\"pages?.length\"\n type=\"button\"\n class=\"mld-topbar-plugin-name\"\n @click.stop=\"togglePagesDropdown\"\n >\n {{ pluginName }}\n <svg\n class=\"mld-topbar-chevron\"\n :class=\"{ 'mld-topbar-chevron--open': showPagesDropdown }\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n <span v-else class=\"mld-topbar-plugin-name--static\">{{ pluginName }}</span>\n\n <!-- Separator -->\n <svg\n v-if=\"title\"\n class=\"mld-topbar-separator\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n\n <!-- Current page title -->\n <span v-if=\"title\" class=\"mld-topbar-current-page\">{{ title }}</span>\n\n <!-- Pages dropdown -->\n <div v-show=\"showPagesDropdown\" class=\"mld-topbar-dropdown\">\n <template v-for=\"page in pages\" :key=\"page.id\">\n <a\n v-if=\"page.href\"\n :href=\"page.href\"\n :class=\"['mld-topbar-dropdown-item', { 'mld-topbar-dropdown-item--active': page.id === currentPageId, 'mld-topbar-dropdown-item--disabled': page.disabled }]\"\n @click=\"showPagesDropdown = false\"\n >\n <span class=\"mld-topbar-dropdown-item__label\">{{ page.label }}</span>\n <span v-if=\"page.description\" class=\"mld-topbar-dropdown-item__description\">{{ page.description }}</span>\n </a>\n <router-link\n v-else-if=\"page.to\"\n :to=\"page.to\"\n :class=\"['mld-topbar-dropdown-item', { 'mld-topbar-dropdown-item--active': page.id === currentPageId, 'mld-topbar-dropdown-item--disabled': page.disabled }]\"\n @click=\"showPagesDropdown = false\"\n >\n <span class=\"mld-topbar-dropdown-item__label\">{{ page.label }}</span>\n <span v-if=\"page.description\" class=\"mld-topbar-dropdown-item__description\">{{ page.description }}</span>\n </router-link>\n <button\n v-else\n type=\"button\"\n :class=\"['mld-topbar-dropdown-item', { 'mld-topbar-dropdown-item--active': page.id === currentPageId, 'mld-topbar-dropdown-item--disabled': page.disabled }]\"\n @click=\"handlePageClick(page)\"\n >\n <span class=\"mld-topbar-dropdown-item__label\">{{ page.label }}</span>\n <span v-if=\"page.description\" class=\"mld-topbar-dropdown-item__description\">{{ page.description }}</span>\n </button>\n </template>\n </div>\n </div>\n\n <!-- Title only (backward compat when no pluginName and no subtitle) -->\n <span v-else-if=\"title\" class=\"mld-topbar__title-only\">{{ title }}</span>\n\n <!-- Navigation slot -->\n <slot name=\"nav\" />\n </div>\n\n <!-- Center section: Tabs -->\n <div v-if=\"tabs?.length\" class=\"mld-topbar__tabs\">\n <template v-for=\"tab in tabs\" :key=\"tab.id\">\n <div\n :ref=\"(el) => tab.children?.length ? setTabDropdownRef(el as HTMLElement, tab.id) : null\"\n class=\"mld-topbar-tab-wrapper\"\n >\n <!-- Tab with children (dropdown) -->\n <button\n v-if=\"tab.children?.length\"\n type=\"button\"\n :class=\"[\n 'mld-topbar-tab',\n { 'mld-topbar-tab--active': tab.id === currentTabId || tab.children.some(c => c.id === currentTabId) },\n { 'mld-topbar-tab--disabled': tab.disabled }\n ]\"\n @click.stop=\"handleTabClick(tab)\"\n >\n {{ tab.label }}\n <svg\n class=\"mld-topbar-tab-chevron\"\n :class=\"{ 'mld-topbar-tab-chevron--open': openTabDropdown === tab.id }\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <!-- Simple tab (no children) -->\n <a\n v-else-if=\"tab.href\"\n :href=\"tab.href\"\n :class=\"[\n 'mld-topbar-tab',\n { 'mld-topbar-tab--active': tab.id === currentTabId },\n { 'mld-topbar-tab--disabled': tab.disabled }\n ]\"\n >\n {{ tab.label }}\n </a>\n <router-link\n v-else-if=\"tab.to\"\n :to=\"tab.to\"\n :class=\"[\n 'mld-topbar-tab',\n { 'mld-topbar-tab--active': tab.id === currentTabId },\n { 'mld-topbar-tab--disabled': tab.disabled }\n ]\"\n >\n {{ tab.label }}\n </router-link>\n <button\n v-else\n type=\"button\"\n :class=\"[\n 'mld-topbar-tab',\n { 'mld-topbar-tab--active': tab.id === currentTabId },\n { 'mld-topbar-tab--disabled': tab.disabled }\n ]\"\n @click=\"handleTabClick(tab)\"\n >\n {{ tab.label }}\n </button>\n\n <!-- Tab dropdown -->\n <div v-if=\"tab.children?.length\" v-show=\"openTabDropdown === tab.id\" class=\"mld-topbar-tab-dropdown\">\n <template v-for=\"option in tab.children\" :key=\"option.id\">\n <a\n v-if=\"option.href\"\n :href=\"option.href\"\n :class=\"[\n 'mld-topbar-dropdown-item',\n { 'mld-topbar-dropdown-item--active': option.id === currentTabId },\n { 'mld-topbar-dropdown-item--disabled': option.disabled }\n ]\"\n @click=\"openTabDropdown = null\"\n >\n <span class=\"mld-topbar-dropdown-item__label\">{{ option.label }}</span>\n <span v-if=\"option.description\" class=\"mld-topbar-dropdown-item__description\">{{ option.description }}</span>\n </a>\n <router-link\n v-else-if=\"option.to\"\n :to=\"option.to\"\n :class=\"[\n 'mld-topbar-dropdown-item',\n { 'mld-topbar-dropdown-item--active': option.id === currentTabId },\n { 'mld-topbar-dropdown-item--disabled': option.disabled }\n ]\"\n @click=\"openTabDropdown = null\"\n >\n <span class=\"mld-topbar-dropdown-item__label\">{{ option.label }}</span>\n <span v-if=\"option.description\" class=\"mld-topbar-dropdown-item__description\">{{ option.description }}</span>\n </router-link>\n <button\n v-else\n type=\"button\"\n :class=\"[\n 'mld-topbar-dropdown-item',\n { 'mld-topbar-dropdown-item--active': option.id === currentTabId },\n { 'mld-topbar-dropdown-item--disabled': option.disabled }\n ]\"\n @click=\"handleTabOptionClick(option, tab)\"\n >\n <span class=\"mld-topbar-dropdown-item__label\">{{ option.label }}</span>\n <span v-if=\"option.description\" class=\"mld-topbar-dropdown-item__description\">{{ option.description }}</span>\n </button>\n </template>\n </div>\n </div>\n </template>\n </div>\n\n <!-- Right section: Actions -->\n <div class=\"mld-topbar__right\">\n <!-- Standalone badge (only when useAppExperiment is NOT active) -->\n <span v-if=\"showStandaloneLabel && isStandalone && !appExperiment\" class=\"mld-topbar__standalone-badge\">\n {{ standaloneLabel }}\n </span>\n\n <!-- Experiment popover (integrated + useAppExperiment active) -->\n <ExperimentPopover\n v-if=\"appExperiment && !isStandalone\"\n :experiment-name=\"appExperiment.experimentName.value\"\n :experiment-status=\"appExperiment.experimentStatus.value\"\n :show-save=\"appExperiment.showSave.value\"\n :show-detach=\"appExperiment.showDetach.value\"\n :save-disabled=\"appExperiment.saveDisabled.value\"\n :save-disabled-message=\"appExperiment.saveDisabledMessage.value\"\n :save-loading=\"appExperiment.saveLoading.value\"\n :save-success-message=\"appExperiment.saveSuccessMessage.value\"\n @select=\"appExperiment.openModal()\"\n @save=\"appExperiment.handleSave()\"\n @detach=\"appExperiment.handleDetach()\"\n />\n\n <!-- Standalone pill (standalone + useAppExperiment active) -->\n <span v-if=\"appExperiment && isStandalone\" class=\"mld-topbar__standalone-badge\">\n {{ standaloneLabel }}\n </span>\n\n <!-- Actions slot (right side) -->\n <slot name=\"actions\" />\n <!-- Theme toggle -->\n <ThemeToggle v-if=\"showThemeToggle\" size=\"sm\" />\n <!-- Settings gear -->\n <button\n v-if=\"showSettings\"\n type=\"button\"\n class=\"mld-topbar__settings-btn\"\n aria-label=\"Open settings\"\n @click=\"settingsOpen = true\"\n >\n <svg class=\"mld-topbar__settings-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915\" /><circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n </button>\n <!-- Admin link -->\n <router-link\n v-if=\"showAdmin\"\n :to=\"adminPath\"\n class=\"mld-topbar__admin-btn\"\n aria-label=\"Admin Dashboard\"\n @click=\"emit('admin-click')\"\n >\n <svg class=\"mld-topbar__admin-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z\" /><path d=\"m9 12 2 2 4-4\" />\n </svg>\n </router-link>\n <!-- Profile button -->\n <button\n v-if=\"showProfile\"\n type=\"button\"\n class=\"mld-topbar__profile-btn\"\n aria-label=\"Edit profile\"\n @click=\"emit('profile-click')\"\n >\n <div class=\"mld-topbar__profile-avatar\">\n {{ profileInitial }}\n </div>\n <span v-if=\"userName\" class=\"mld-topbar__profile-name\">{{ userName }}</span>\n </button>\n </div>\n </div>\n </header>\n\n <SettingsModal\n v-if=\"showSettings\"\n v-model=\"settingsOpen\"\n :title=\"settingsConfig?.title\"\n :tabs=\"settingsConfig?.tabs\"\n :show-appearance=\"settingsConfig?.showAppearance ?? true\"\n :size=\"settingsConfig?.size\"\n >\n <template v-for=\"tab in (settingsConfig?.tabs ?? [])\" :key=\"tab.id\" #[`tab-${tab.id}`]>\n <slot :name=\"`settings-tab-${tab.id}`\" />\n </template>\n <template #appearance>\n <slot name=\"settings-appearance\" />\n </template>\n </SettingsModal>\n\n <ExperimentSelectorModal\n v-if=\"appExperiment && !isStandalone\"\n :model-value=\"appExperiment.showModal.value\"\n :current-experiment-id=\"appExperiment.experimentId.value\"\n @update:model-value=\"$event ? appExperiment.openModal() : appExperiment.closeModal()\"\n @select=\"appExperiment.handleSelect($event)\"\n @deselect=\"appExperiment.handleDetach()\"\n />\n</template>\n\n<style>\n@import '../styles/components/app-top-bar.css';\n</style>\n"],"names":["_createElementVNode","_normalizeClass","_createElementBlock","_renderSlot","_openBlock","_createBlock","_toDisplayString","_createTextVNode","_withDirectives","_Fragment","_renderList","_a","_withModifiers","_b","_unref","ExperimentPopover","ThemeToggle","SettingsModal","ExperimentSelectorModal"],"mappings":"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"}
@@ -25,9 +25,9 @@ declare const __VLS_component: import('vue').DefineComponent<Props, {}, {}, {},
25
25
  "onEntry-click"?: ((entry: AuditEntry) => any) | undefined;
26
26
  }>, {
27
27
  size: "sm" | "md" | "lg";
28
- emptyMessage: string;
29
28
  order: "newest" | "oldest";
30
29
  showFilters: boolean;
30
+ emptyMessage: string;
31
31
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
32
32
  declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
33
33
  export default _default;
@@ -19,9 +19,9 @@ declare const _default: import('vue').DefineComponent<Props, {}, {}, {}, {}, imp
19
19
  }>, {
20
20
  size: ModalSize;
21
21
  title: string;
22
- showFilters: boolean;
23
22
  currentExperimentId: number | null;
24
23
  groupByProject: boolean;
24
+ showFilters: boolean;
25
25
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
26
26
  listRef: HTMLDivElement;
27
27
  }, any>;
@@ -6,33 +6,37 @@ const _hoisted_3 = {
6
6
  key: 0,
7
7
  class: "mld-formula-input__preview"
8
8
  };
9
- const _hoisted_4 = { key: 0 };
10
- const _hoisted_5 = {
9
+ const _hoisted_4 = {
10
+ key: 0,
11
+ class: "mld-formula-input__preview-formula"
12
+ };
13
+ const _hoisted_5 = { key: 0 };
14
+ const _hoisted_6 = {
11
15
  key: 1,
12
16
  style: { "vertical-align": "sub", "font-size": "0.75em", "line-height": "0" }
13
17
  };
14
- const _hoisted_6 = {
18
+ const _hoisted_7 = {
15
19
  key: 2,
16
20
  style: { "vertical-align": "super", "font-size": "0.75em", "line-height": "0" }
17
21
  };
18
- const _hoisted_7 = {
22
+ const _hoisted_8 = {
19
23
  key: 3,
20
24
  style: { "color": "var(--text-secondary)" }
21
25
  };
22
- const _hoisted_8 = {
26
+ const _hoisted_9 = {
23
27
  key: 4,
24
28
  style: { "margin": "0 0.125em", "color": "var(--text-muted)" }
25
29
  };
26
- const _hoisted_9 = {
30
+ const _hoisted_10 = {
27
31
  key: 5,
28
32
  style: { "vertical-align": "super", "font-size": "0.75em", "line-height": "0" }
29
33
  };
30
- const _hoisted_10 = {
31
- key: 0,
32
- class: "mld-formula-input__mw"
33
- };
34
34
  const _hoisted_11 = {
35
35
  key: 1,
36
+ class: "mld-formula-input__mw"
37
+ };
38
+ const _hoisted_12 = {
39
+ key: 0,
36
40
  class: "mld-formula-input__error-text"
37
41
  };
38
42
  const _sfc_main = /* @__PURE__ */ defineComponent({
@@ -101,16 +105,18 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
101
105
  "aria-label": "Chemical formula",
102
106
  onInput: handleInput
103
107
  }, null, 42, _hoisted_2),
104
- __props.showPreview && __props.modelValue ? (openBlock(), createElementBlock("div", _hoisted_3, [
105
- (openBlock(true), createElementBlock(Fragment, null, renderList(getParts(__props.modelValue), (part, i) => {
106
- return openBlock(), createElementBlock(Fragment, { key: i }, [
107
- part.type === "element" ? (openBlock(), createElementBlock("span", _hoisted_4, toDisplayString(part.text), 1)) : part.type === "subscript" ? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString(part.text), 1)) : part.type === "superscript" ? (openBlock(), createElementBlock("span", _hoisted_6, toDisplayString(part.text), 1)) : part.type === "paren" ? (openBlock(), createElementBlock("span", _hoisted_7, toDisplayString(part.text), 1)) : part.type === "dot" ? (openBlock(), createElementBlock("span", _hoisted_8, toDisplayString(part.text), 1)) : part.type === "charge" ? (openBlock(), createElementBlock("span", _hoisted_9, toDisplayString(part.text), 1)) : createCommentVNode("", true)
108
- ], 64);
109
- }), 128))
108
+ __props.showPreview && __props.modelValue || __props.showMW && __props.modelValue && !getError(__props.modelValue) && getMW(__props.modelValue) !== null ? (openBlock(), createElementBlock("div", _hoisted_3, [
109
+ __props.showPreview && __props.modelValue ? (openBlock(), createElementBlock("span", _hoisted_4, [
110
+ (openBlock(true), createElementBlock(Fragment, null, renderList(getParts(__props.modelValue), (part, i) => {
111
+ return openBlock(), createElementBlock(Fragment, { key: i }, [
112
+ part.type === "element" ? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString(part.text), 1)) : part.type === "subscript" ? (openBlock(), createElementBlock("span", _hoisted_6, toDisplayString(part.text), 1)) : part.type === "superscript" ? (openBlock(), createElementBlock("span", _hoisted_7, toDisplayString(part.text), 1)) : part.type === "paren" ? (openBlock(), createElementBlock("span", _hoisted_8, toDisplayString(part.text), 1)) : part.type === "dot" ? (openBlock(), createElementBlock("span", _hoisted_9, toDisplayString(part.text), 1)) : part.type === "charge" ? (openBlock(), createElementBlock("span", _hoisted_10, toDisplayString(part.text), 1)) : createCommentVNode("", true)
113
+ ], 64);
114
+ }), 128))
115
+ ])) : createCommentVNode("", true),
116
+ __props.showMW && __props.modelValue && !getError(__props.modelValue) && getMW(__props.modelValue) !== null ? (openBlock(), createElementBlock("span", _hoisted_11, toDisplayString(formatMW(__props.modelValue)), 1)) : createCommentVNode("", true)
110
117
  ])) : createCommentVNode("", true)
111
118
  ]),
112
- __props.showMW && __props.modelValue && !getError(__props.modelValue) && getMW(__props.modelValue) !== null ? (openBlock(), createElementBlock("div", _hoisted_10, toDisplayString(formatMW(__props.modelValue)), 1)) : createCommentVNode("", true),
113
- __props.modelValue && getError(__props.modelValue) ? (openBlock(), createElementBlock("div", _hoisted_11, toDisplayString(getError(__props.modelValue)), 1)) : createCommentVNode("", true)
119
+ __props.modelValue && getError(__props.modelValue) ? (openBlock(), createElementBlock("div", _hoisted_12, toDisplayString(getError(__props.modelValue)), 1)) : createCommentVNode("", true)
114
120
  ], 2);
115
121
  };
116
122
  }
@@ -1 +1 @@
1
- {"version":3,"file":"FormulaInput.vue.js","sources":["../../src/components/FormulaInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useChemicalFormula, type FormulaPart } from '../composables/useChemicalFormula'\n\ninterface Props {\n modelValue?: string\n showPreview?: boolean\n showMW?: boolean\n placeholder?: string\n error?: boolean\n disabled?: boolean\n size?: 'sm' | 'md' | 'lg'\n}\n\nwithDefaults(defineProps<Props>(), {\n modelValue: '',\n showPreview: true,\n showMW: true,\n placeholder: 'e.g. Ca(OH)2',\n error: false,\n disabled: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n 'mw': [mw: number | null]\n}>()\n\nconst { parseFormula, calculateMW, renderFormulaParts } = useChemicalFormula()\n\nfunction getParseResult(value: string) {\n if (!value) return null\n return parseFormula(value)\n}\n\nfunction getMW(value: string): number | null {\n const result = getParseResult(value)\n if (!result || !result.valid) return null\n return calculateMW(result.elements)\n}\n\nfunction getParts(value: string): FormulaPart[] {\n if (!value) return []\n return renderFormulaParts(value)\n}\n\nfunction getError(value: string): string | null {\n if (!value) return null\n const result = getParseResult(value)\n if (!result) return null\n if (!result.valid) return result.error || 'Invalid formula'\n return null\n}\n\nfunction formatMW(value: string): string {\n const mw = getMW(value)\n if (mw === null) return ''\n return `${mw.toFixed(2)} g/mol`\n}\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n emit('update:modelValue', target.value)\n emit('mw', getMW(target.value))\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mld-formula-input',\n error ? 'mld-formula-input--error' : '',\n disabled ? 'mld-formula-input--disabled' : '',\n ]\"\n >\n <div class=\"mld-formula-input__field\">\n <input\n type=\"text\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :class=\"[\n 'mld-formula-input__input',\n `mld-formula-input__input--${size}`,\n ]\"\n aria-label=\"Chemical formula\"\n @input=\"handleInput\"\n />\n\n <div\n v-if=\"showPreview && modelValue\"\n class=\"mld-formula-input__preview\"\n >\n <template v-for=\"(part, i) in getParts(modelValue)\" :key=\"i\">\n <span v-if=\"part.type === 'element'\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'subscript'\" style=\"vertical-align: sub; font-size: 0.75em; line-height: 0;\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'superscript'\" style=\"vertical-align: super; font-size: 0.75em; line-height: 0;\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'paren'\" style=\"color: var(--text-secondary);\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'dot'\" style=\"margin: 0 0.125em; color: var(--text-muted);\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'charge'\" style=\"vertical-align: super; font-size: 0.75em; line-height: 0;\">{{ part.text }}</span>\n </template>\n </div>\n </div>\n\n <div\n v-if=\"showMW && modelValue && !getError(modelValue) && getMW(modelValue) !== null\"\n class=\"mld-formula-input__mw\"\n >\n {{ formatMW(modelValue) }}\n </div>\n\n <div\n v-if=\"modelValue && getError(modelValue)\"\n class=\"mld-formula-input__error-text\"\n >\n {{ getError(modelValue) }}\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/formula-input.css';\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode","_openBlock","_Fragment","_toDisplayString"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,UAAM,OAAO;AAKb,UAAM,EAAE,cAAc,aAAa,mBAAA,IAAuB,mBAAA;AAE1D,aAAS,eAAe,OAAe;AACrC,UAAI,CAAC,MAAO,QAAO;AACnB,aAAO,aAAa,KAAK;AAAA,IAC3B;AAEA,aAAS,MAAM,OAA8B;AAC3C,YAAM,SAAS,eAAe,KAAK;AACnC,UAAI,CAAC,UAAU,CAAC,OAAO,MAAO,QAAO;AACrC,aAAO,YAAY,OAAO,QAAQ;AAAA,IACpC;AAEA,aAAS,SAAS,OAA8B;AAC9C,UAAI,CAAC,MAAO,QAAO,CAAA;AACnB,aAAO,mBAAmB,KAAK;AAAA,IACjC;AAEA,aAAS,SAAS,OAA8B;AAC9C,UAAI,CAAC,MAAO,QAAO;AACnB,YAAM,SAAS,eAAe,KAAK;AACnC,UAAI,CAAC,OAAQ,QAAO;AACpB,UAAI,CAAC,OAAO,MAAO,QAAO,OAAO,SAAS;AAC1C,aAAO;AAAA,IACT;AAEA,aAAS,SAAS,OAAuB;AACvC,YAAM,KAAK,MAAM,KAAK;AACtB,UAAI,OAAO,KAAM,QAAO;AACxB,aAAO,GAAG,GAAG,QAAQ,CAAC,CAAC;AAAA,IACzB;AAEA,aAAS,YAAY,OAAc;AACjC,YAAM,SAAS,MAAM;AACrB,WAAK,qBAAqB,OAAO,KAAK;AACtC,WAAK,MAAM,MAAM,OAAO,KAAK,CAAC;AAAA,IAChC;;0BAIEA,mBAiDM,OAAA;AAAA,QAhDH,OAAKC,eAAA;AAAA;UAAqC,QAAA,QAAK,6BAAA;AAAA,UAA0C,QAAA,WAAQ,gCAAA;AAAA,QAAA;;QAMlGC,mBA2BM,OA3BN,YA2BM;AAAA,UA1BJA,mBAWE,SAAA;AAAA,YAVA,MAAK;AAAA,YACJ,OAAO,QAAA;AAAA,YACP,aAAa,QAAA;AAAA,YACb,UAAU,QAAA;AAAA,YACV,OAAKD,eAAA;AAAA;2CAAiF,QAAA,IAAI;AAAA,YAAA;YAI3F,cAAW;AAAA,YACV,SAAO;AAAA,UAAA;UAIF,QAAA,eAAe,QAAA,cADvBE,aAAAH,mBAYM,OAZN,YAYM;AAAA,aARJG,UAAA,IAAA,GAAAH,mBAOWI,2BAPmB,SAAS,kBAAU,GAAA,CAA/B,MAAM,MAAC;sEAAiC,KAAC;AAAA,gBAC7C,KAAK,SAAI,0BAArBJ,mBAA2D,QAAA,YAAAK,gBAAnB,KAAK,IAAI,GAAA,CAAA,KAChC,KAAK,SAAI,eAA1BF,aAAAH,mBAAkI,QAAlI,YAAkIK,gBAAnB,KAAK,IAAI,GAAA,CAAA,KACvG,KAAK,SAAI,iBAA1BF,aAAAH,mBAAsI,QAAtI,YAAsIK,gBAAnB,KAAK,IAAI,GAAA,CAAA,KAC3G,KAAK,SAAI,WAA1BF,aAAAH,mBAAoG,QAApG,YAAoGK,gBAAnB,KAAK,IAAI,GAAA,CAAA,KACzE,KAAK,SAAI,SAA1BF,aAAAH,mBAAiH,QAAjH,YAAiHK,gBAAnB,KAAK,IAAI,GAAA,CAAA,KACtF,KAAK,SAAI,YAA1BF,aAAAH,mBAAiI,QAAjI,YAAiIK,gBAAnB,KAAK,IAAI,GAAA,CAAA;;;;;QAMrH,QAAA,UAAU,sBAAU,CAAK,SAAS,QAAA,UAAU,KAAK,MAAM,QAAA,UAAU,MAAA,QADzEF,aAAAH,mBAKM,OALN,aAKMK,gBADD,SAAS,QAAA,UAAU,CAAA,GAAA,CAAA;QAIhB,QAAA,cAAc,SAAS,QAAA,UAAU,KADzCF,UAAA,GAAAH,mBAKM,OALN,aAKMK,gBADD,SAAS,QAAA,UAAU,CAAA,GAAA,CAAA;;;;;"}
1
+ {"version":3,"file":"FormulaInput.vue.js","sources":["../../src/components/FormulaInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useChemicalFormula, type FormulaPart } from '../composables/useChemicalFormula'\n\ninterface Props {\n modelValue?: string\n showPreview?: boolean\n showMW?: boolean\n placeholder?: string\n error?: boolean\n disabled?: boolean\n size?: 'sm' | 'md' | 'lg'\n}\n\nwithDefaults(defineProps<Props>(), {\n modelValue: '',\n showPreview: true,\n showMW: true,\n placeholder: 'e.g. Ca(OH)2',\n error: false,\n disabled: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n 'mw': [mw: number | null]\n}>()\n\nconst { parseFormula, calculateMW, renderFormulaParts } = useChemicalFormula()\n\nfunction getParseResult(value: string) {\n if (!value) return null\n return parseFormula(value)\n}\n\nfunction getMW(value: string): number | null {\n const result = getParseResult(value)\n if (!result || !result.valid) return null\n return calculateMW(result.elements)\n}\n\nfunction getParts(value: string): FormulaPart[] {\n if (!value) return []\n return renderFormulaParts(value)\n}\n\nfunction getError(value: string): string | null {\n if (!value) return null\n const result = getParseResult(value)\n if (!result) return null\n if (!result.valid) return result.error || 'Invalid formula'\n return null\n}\n\nfunction formatMW(value: string): string {\n const mw = getMW(value)\n if (mw === null) return ''\n return `${mw.toFixed(2)} g/mol`\n}\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n emit('update:modelValue', target.value)\n emit('mw', getMW(target.value))\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mld-formula-input',\n error ? 'mld-formula-input--error' : '',\n disabled ? 'mld-formula-input--disabled' : '',\n ]\"\n >\n <div class=\"mld-formula-input__field\">\n <input\n type=\"text\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :class=\"[\n 'mld-formula-input__input',\n `mld-formula-input__input--${size}`,\n ]\"\n aria-label=\"Chemical formula\"\n @input=\"handleInput\"\n />\n\n <div\n v-if=\"(showPreview && modelValue) || (showMW && modelValue && !getError(modelValue) && getMW(modelValue) !== null)\"\n class=\"mld-formula-input__preview\"\n >\n <span v-if=\"showPreview && modelValue\" class=\"mld-formula-input__preview-formula\">\n <template v-for=\"(part, i) in getParts(modelValue)\" :key=\"i\">\n <span v-if=\"part.type === 'element'\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'subscript'\" style=\"vertical-align: sub; font-size: 0.75em; line-height: 0;\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'superscript'\" style=\"vertical-align: super; font-size: 0.75em; line-height: 0;\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'paren'\" style=\"color: var(--text-secondary);\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'dot'\" style=\"margin: 0 0.125em; color: var(--text-muted);\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'charge'\" style=\"vertical-align: super; font-size: 0.75em; line-height: 0;\">{{ part.text }}</span>\n </template>\n </span>\n <span\n v-if=\"showMW && modelValue && !getError(modelValue) && getMW(modelValue) !== null\"\n class=\"mld-formula-input__mw\"\n >\n {{ formatMW(modelValue) }}\n </span>\n </div>\n </div>\n\n <div\n v-if=\"modelValue && getError(modelValue)\"\n class=\"mld-formula-input__error-text\"\n >\n {{ getError(modelValue) }}\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/formula-input.css';\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode","_openBlock","_Fragment","_toDisplayString"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,UAAM,OAAO;AAKb,UAAM,EAAE,cAAc,aAAa,mBAAA,IAAuB,mBAAA;AAE1D,aAAS,eAAe,OAAe;AACrC,UAAI,CAAC,MAAO,QAAO;AACnB,aAAO,aAAa,KAAK;AAAA,IAC3B;AAEA,aAAS,MAAM,OAA8B;AAC3C,YAAM,SAAS,eAAe,KAAK;AACnC,UAAI,CAAC,UAAU,CAAC,OAAO,MAAO,QAAO;AACrC,aAAO,YAAY,OAAO,QAAQ;AAAA,IACpC;AAEA,aAAS,SAAS,OAA8B;AAC9C,UAAI,CAAC,MAAO,QAAO,CAAA;AACnB,aAAO,mBAAmB,KAAK;AAAA,IACjC;AAEA,aAAS,SAAS,OAA8B;AAC9C,UAAI,CAAC,MAAO,QAAO;AACnB,YAAM,SAAS,eAAe,KAAK;AACnC,UAAI,CAAC,OAAQ,QAAO;AACpB,UAAI,CAAC,OAAO,MAAO,QAAO,OAAO,SAAS;AAC1C,aAAO;AAAA,IACT;AAEA,aAAS,SAAS,OAAuB;AACvC,YAAM,KAAK,MAAM,KAAK;AACtB,UAAI,OAAO,KAAM,QAAO;AACxB,aAAO,GAAG,GAAG,QAAQ,CAAC,CAAC;AAAA,IACzB;AAEA,aAAS,YAAY,OAAc;AACjC,YAAM,SAAS,MAAM;AACrB,WAAK,qBAAqB,OAAO,KAAK;AACtC,WAAK,MAAM,MAAM,OAAO,KAAK,CAAC;AAAA,IAChC;;0BAIEA,mBAkDM,OAAA;AAAA,QAjDH,OAAKC,eAAA;AAAA;UAAqC,QAAA,QAAK,6BAAA;AAAA,UAA0C,QAAA,WAAQ,gCAAA;AAAA,QAAA;;QAMlGC,mBAmCM,OAnCN,YAmCM;AAAA,UAlCJA,mBAWE,SAAA;AAAA,YAVA,MAAK;AAAA,YACJ,OAAO,QAAA;AAAA,YACP,aAAa,QAAA;AAAA,YACb,UAAU,QAAA;AAAA,YACV,OAAKD,eAAA;AAAA;2CAAiF,QAAA,IAAI;AAAA,YAAA;YAI3F,cAAW;AAAA,YACV,SAAO;AAAA,UAAA;UAID,QAAA,eAAe,QAAA,cAAgB,QAAA,UAAU,QAAA,cAAU,CAAK,SAAS,QAAA,UAAU,KAAK,MAAM,QAAA,UAAU,MAAA,QADzGE,aAAAH,mBAoBM,OApBN,YAoBM;AAAA,YAhBQ,QAAA,eAAe,QAAA,cAA3BG,aAAAH,mBASO,QATP,YASO;AAAA,eARLG,UAAA,IAAA,GAAAH,mBAOWI,2BAPmB,SAAS,kBAAU,GAAA,CAA/B,MAAM,MAAC;wEAAiC,KAAC;AAAA,kBAC7C,KAAK,SAAI,0BAArBJ,mBAA2D,QAAA,YAAAK,gBAAnB,KAAK,IAAI,GAAA,CAAA,KAChC,KAAK,SAAI,eAA1BF,aAAAH,mBAAkI,QAAlI,YAAkIK,gBAAnB,KAAK,IAAI,GAAA,CAAA,KACvG,KAAK,SAAI,iBAA1BF,aAAAH,mBAAsI,QAAtI,YAAsIK,gBAAnB,KAAK,IAAI,GAAA,CAAA,KAC3G,KAAK,SAAI,WAA1BF,aAAAH,mBAAoG,QAApG,YAAoGK,gBAAnB,KAAK,IAAI,GAAA,CAAA,KACzE,KAAK,SAAI,SAA1BF,aAAAH,mBAAiH,QAAjH,YAAiHK,gBAAnB,KAAK,IAAI,GAAA,CAAA,KACtF,KAAK,SAAI,YAA1BF,aAAAH,mBAAiI,QAAjI,aAAiIK,gBAAnB,KAAK,IAAI,GAAA,CAAA;;;;YAInH,QAAA,UAAU,sBAAU,CAAK,SAAS,QAAA,UAAU,KAAK,MAAM,QAAA,UAAU,MAAA,QADzEF,UAAA,GAAAH,mBAKO,QALP,aAKOK,gBADF,SAAS,QAAA,UAAU,CAAA,GAAA,CAAA;;;QAMpB,QAAA,cAAc,SAAS,QAAA,UAAU,KADzCF,UAAA,GAAAH,mBAKM,OALN,aAKMK,gBADD,SAAS,QAAA,UAAU,CAAA,GAAA,CAAA;;;;;"}
@@ -108,27 +108,37 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
108
108
  });
109
109
  return win.__jsmeLoadPromise__;
110
110
  }
111
+ function waitForPaint() {
112
+ return new Promise((resolve) => {
113
+ requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
114
+ });
115
+ }
111
116
  async function initJSME() {
112
117
  var _a, _b;
113
- if (!containerRef.value || props.readonly) return;
118
+ if (props.readonly) return;
114
119
  try {
115
120
  isLoading.value = true;
116
121
  loadError.value = null;
117
122
  await waitForJSME();
118
- await nextTick();
119
- if (!containerRef.value) return;
120
123
  const win = getJSMEState();
121
124
  if (!((_a = win.JSApplet) == null ? void 0 : _a.JSME)) {
122
125
  throw new Error("JSME library not available after loading");
123
126
  }
127
+ isLoading.value = false;
128
+ await nextTick();
129
+ if (!containerRef.value) return;
130
+ await waitForPaint();
131
+ if (!containerRef.value) return;
132
+ const rect = containerRef.value.getBoundingClientRect();
133
+ const width = Math.floor(rect.width) || 400;
124
134
  const editorId = `jsme-${Date.now()}`;
125
135
  containerRef.value.id = editorId;
126
136
  const instance = new win.JSApplet.JSME(
127
137
  editorId,
128
- "100%",
138
+ `${width}px`,
129
139
  `${props.height}px`,
130
140
  {
131
- options: "query,hydrogens,paste,depict"
141
+ options: "query,hydrogens,paste"
132
142
  }
133
143
  );
134
144
  jsmeInstance.value = instance;
@@ -136,7 +146,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
136
146
  instance.readMolFile(props.modelValue.molfile);
137
147
  }
138
148
  instance.setCallBack("AfterStructureModified", handleStructureChange);
139
- isLoading.value = false;
140
149
  } catch (err) {
141
150
  const message = err instanceof Error ? err.message : "Failed to load molecule editor";
142
151
  loadError.value = message;