@tekkare/romulus 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.16.0"
6
6
  },
7
- "version": "0.1.10",
7
+ "version": "0.1.12",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -21,6 +21,7 @@ const module = defineNuxtModule({
21
21
  if (options.baseStyles)
22
22
  nuxt.options.css.push(resolve(runtimeDir, "assets/styles/base.css"));
23
23
  nuxt.options.css.push(resolve(runtimeDir, "assets/styles/components.css"));
24
+ nuxt.options.css.push(resolve(runtimeDir, "assets/styles/chrome.css"));
24
25
  if (options.utilities)
25
26
  nuxt.options.css.push(resolve(runtimeDir, "assets/styles/utilities.css"));
26
27
  await addComponentsDir({
@@ -38,6 +39,10 @@ const module = defineNuxtModule({
38
39
  { rel: "stylesheet", href: fontsHref }
39
40
  );
40
41
  }
42
+ const phosphorHref = "https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/style.css";
43
+ if (!nuxt.options.app.head.link.some((l) => l.href === phosphorHref)) {
44
+ nuxt.options.app.head.link.push({ rel: "stylesheet", href: phosphorHref });
45
+ }
41
46
  }
42
47
  });
43
48
 
@@ -0,0 +1 @@
1
+ body.lcollapsed{padding-left:var(--sidebar-collapsed-w)}.nav-item .icn i,.rpanel-item .icn i{font-size:16px}.strip-icon i,.topbar-toggle i{font-size:18px}.chev i,.chev-r i{font-size:11px}.topbar-toggle{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:6px;color:var(--text-secondary);cursor:pointer;display:flex;font-size:16px;height:32px;justify-content:center;width:32px}.topbar-toggle:hover{background:var(--bg-hover);border-color:var(--color-violet-pale);color:var(--color-secondary)}.logo-mark{align-items:center;background:linear-gradient(135deg,var(--color-primary),var(--color-secondary));border-radius:6px;box-shadow:0 2px 6px rgba(107,63,255,.25);color:var(--color-text-inverse);display:flex;flex-shrink:0;font-size:14px;height:28px;justify-content:center;width:28px}.logo-mark,.logo-text{font-family:var(--font-serif);font-weight:700}.logo-text{font-size:16px;line-height:1}.logo-sub{color:var(--text-muted);font-size:10px;letter-spacing:.05em;margin-top:2px;text-transform:uppercase}.sidebar{background:var(--bg-surface);border-right:1px solid var(--border-default);bottom:0;display:flex;flex-direction:column;left:0;overflow-x:hidden;overflow-y:auto;position:fixed;top:0;transition:width .24s cubic-bezier(.2,0,0,1);width:var(--sidebar-w);z-index:70}.sidebar-top{border-bottom:1px solid var(--border-default);flex-shrink:0;height:var(--topbar-h);justify-content:space-between;padding:0 14px}.sidebar-top,.sidebar-top .logo-block{align-items:center;display:flex;gap:8px}.sidebar-top .logo-block{min-width:0}body.lcollapsed .sidebar-top{justify-content:center;padding:0}body.lcollapsed .sidebar-top .logo-block{display:none}body.lcollapsed .sidebar-top .logo-mark{display:flex}.sidebar-search{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:999px;display:flex;gap:10px;margin:10px 12px 4px;padding:7px 14px;transition:border-color .15s,background .15s}.sidebar-search:focus-within,.sidebar-search:hover{background:var(--bg-surface);border-color:var(--color-violet-pale)}.sidebar-search i{color:var(--text-muted);flex-shrink:0;font-size:14px}.sidebar-search input{background:transparent;border:none;color:var(--text-primary);flex:1;font-family:inherit;font-size:13px;min-width:0;outline:none}.sidebar-search input::-moz-placeholder{color:var(--text-muted)}.sidebar-search input::placeholder{color:var(--text-muted)}.sidebar-search kbd{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:4px;color:var(--text-secondary);flex-shrink:0;font-family:inherit;font-size:10px;font-weight:600;padding:1px 5px}body.lcollapsed .sidebar-search{display:none}body.lcollapsed .sidebar{width:var(--sidebar-collapsed-w)}.nav-group{padding:4px 0}.nav-section-header{align-items:center;cursor:pointer;display:flex;justify-content:space-between;padding:14px 14px 6px;-webkit-user-select:none;-moz-user-select:none;user-select:none}.nav-section-header .label,.nav-section-header span{color:var(--text-muted);font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.nav-section-header .chev{color:var(--text-muted);font-size:10px;transition:transform .2s}.nav-section.collapsed .chev{transform:rotate(-90deg)}.nav-section.collapsed .nav-items{display:none}.nav-item{align-items:center;color:var(--text-secondary);cursor:pointer;display:flex;font-size:13px;font-weight:500;gap:10px;padding:7px 14px 7px 22px;transition:background .15s,color .15s;-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:nowrap}.nav-item .icn{color:var(--text-muted);flex-shrink:0;text-align:center;width:16px}.nav-item .lbl{flex:1;overflow:hidden;text-overflow:ellipsis}.nav-item .chev-r{color:var(--text-muted);font-size:10px;transition:transform .2s}.nav-item.has-children.open .chev-r{transform:rotate(90deg)}.nav-item:hover{background:var(--bg-surface-3);color:var(--text-primary)}.nav-item:hover .icn{color:var(--text-secondary)}.nav-item.active{background:linear-gradient(90deg,var(--color-violet-pale) 0,var(--bg-hover) 100%);color:var(--color-primary);font-weight:700;position:relative}.nav-item.active:before{background:var(--color-secondary);border-radius:0 4px 4px 0;bottom:5px;content:"";left:0;position:absolute;top:5px;width:4px}.nav-item.active .icn{color:var(--color-secondary)}.nav-subitems{background:var(--bg-surface-2);border-bottom:1px solid var(--border-soft);border-top:1px solid var(--border-soft);display:none;padding:2px 0 4px}.nav-item.has-children.open+.nav-subitems{display:block}.nav-subitem{align-items:center;color:var(--text-secondary);cursor:pointer;display:flex;font-size:12px;gap:8px;padding:6px 14px 6px 44px}.nav-subitem:hover{background:var(--bg-surface-3);color:var(--text-primary)}.nav-subitem.active{background:linear-gradient(90deg,var(--color-violet-pale) 0,var(--bg-hover) 100%);color:var(--color-primary);font-weight:700;position:relative}.nav-subitem.active:before{background:var(--color-secondary);border-radius:0 4px 4px 0;bottom:4px;content:"";left:0;position:absolute;top:4px;width:4px}.sidebar-footer{border-top:1px solid var(--border-default);display:flex;flex-direction:column;gap:4px;margin-top:auto;padding:10px 8px}.sb-user{align-items:center;border-radius:8px;cursor:pointer;display:flex;gap:12px;padding:10px 12px;transition:background .15s;-webkit-user-select:none;-moz-user-select:none;user-select:none}.sb-user:hover{background:var(--bg-surface-3)}.sb-user-avatar{align-items:center;background:linear-gradient(135deg,var(--color-secondary),var(--color-accent-teal));border-radius:50%;box-shadow:0 2px 6px rgba(107,63,255,.22);color:var(--color-text-inverse);display:flex;flex-shrink:0;font-family:var(--font-serif);font-size:13px;font-weight:700;height:36px;justify-content:center;width:36px}.sb-user-info{flex:1;min-width:0}.sb-user-name{color:var(--text-primary);font-size:13px;font-weight:600;line-height:1.25}.sb-user-name,.sb-user-org{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sb-user-org{color:var(--text-muted);font-size:11px;line-height:1.2;margin-top:2px}.sb-user-chev{color:var(--text-muted);flex-shrink:0;font-size:13px}.sb-actions{display:flex;gap:4px}.sb-actions .sb-lang,.sb-actions a{align-items:center;border-radius:6px;color:var(--text-secondary);cursor:pointer;display:inline-flex;flex:1;font-family:inherit;font-size:12px;font-weight:600;gap:5px;justify-content:center;padding:8px 10px;text-decoration:none;transition:background .15s,color .15s}.sb-actions .sb-lang:hover,.sb-actions a:hover{background:var(--bg-surface-3);color:var(--text-primary)}.sb-actions a.primary{color:var(--color-primary)}.sb-actions a.primary:hover{background:var(--bg-hover);color:var(--color-secondary)}.sb-actions i{font-size:13px}body.lcollapsed .sidebar .chev-r,body.lcollapsed .sidebar .lbl,body.lcollapsed .sidebar .nav-section-header,body.lcollapsed .sidebar-footer{display:none}body.lcollapsed .sidebar .nav-item.has-children.open+.nav-subitems,body.lcollapsed .sidebar .nav-subitems{display:none!important}body.lcollapsed .sidebar .nav-item.has-children.open .chev-r{transform:none}body.lcollapsed .sidebar .nav-group{border-top:1px solid var(--border-soft);padding:4px 0}body.lcollapsed .sidebar .nav-group:first-of-type{border-top:none}.nav-subitems.flyout,body.lcollapsed .sidebar .nav-item.has-children.open+.nav-subitems.flyout,body.lcollapsed .sidebar .nav-subitems.flyout{animation:flyout-in .12s cubic-bezier(.2,0,0,1);background:var(--bg-surface);border:1px solid var(--border-default);border-left:none;border-radius:0 8px 8px 0;box-shadow:var(--shadow-lg);display:block!important;left:var(--sidebar-collapsed-w);min-width:220px;padding:8px 0;position:fixed;z-index:100}.nav-subitems.flyout:before{border-bottom:1px solid var(--border-soft);color:var(--text-muted);content:attr(data-parent-label);display:block;font-size:11px;font-weight:700;letter-spacing:.08em;margin-bottom:4px;padding:6px 16px 8px;text-transform:uppercase}.nav-subitems.flyout .nav-subitem{font-size:13px;padding:8px 16px}body.lcollapsed .sidebar .nav-item{position:relative}body.lcollapsed .sidebar .nav-item[data-tip]:hover:after{background:var(--text-primary);border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.2);color:var(--color-text-inverse);content:attr(data-tip);font-size:12px;font-weight:500;left:var(--sidebar-collapsed-w);margin-left:6px;padding:5px 10px;pointer-events:none;position:fixed;top:var(--tip-top,0);white-space:nowrap;z-index:110}.rsidebar .strip-icon{position:relative}.rsidebar .strip-icon[data-tip]:hover:before{background:var(--text-primary);border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.2);color:var(--color-text-inverse);content:attr(data-tip);font-size:12px;font-weight:500;margin-right:6px;padding:5px 10px;pointer-events:none;position:fixed;right:var(--rsidebar-collapsed-w);top:var(--tip-top,0);white-space:nowrap;z-index:110}body.ropen .rsidebar .strip-icon[data-tip]:hover:before{display:none}.rsidebar .strip-icon.has-flyout.show-flyout .rflyout{display:block}body.ropen .rsidebar .rflyout{display:none!important}body.lcollapsed .nav-item{justify-content:center;padding:10px 0}body.lcollapsed .nav-section-header{justify-content:center;padding:8px 0}.rsidebar{background:var(--bg-surface);border-left:1px solid var(--border-default);bottom:0;display:flex;flex-direction:column;overflow:hidden;position:fixed;right:0;top:0;width:var(--rsidebar-collapsed-w);z-index:70}.rstrip-top{align-items:center;border-bottom:1px solid var(--border-default);display:flex;flex-shrink:0;height:var(--topbar-h);justify-content:center}.rstrip-collapsed{align-items:stretch;gap:4px;padding:8px}.rstrip-collapsed,.strip-icon{display:flex;flex-direction:column}.strip-icon{align-items:center;border-radius:8px;color:var(--text-secondary);cursor:pointer;gap:5px;justify-content:center;padding:10px 4px;position:relative;transition:background .15s,color .15s}.strip-icon i{font-size:19px;transition:color .15s}.strip-icon .strip-label{color:inherit;font-size:10px;font-weight:600;letter-spacing:.01em;line-height:1;white-space:nowrap}.strip-icon:hover{background:var(--bg-surface-3);color:var(--text-primary)}.strip-icon.active,.strip-icon:hover i{color:var(--color-secondary)}.strip-icon.active{background:var(--bg-hover)}.strip-icon.active i{color:var(--color-secondary)}.strip-icon .dot{background:var(--color-notification);border-radius:50%;box-shadow:0 0 0 2px var(--color-text-inverse);height:8px;position:absolute;right:14px;top:6px;width:8px}.strip-icon.is-live .dot{background:var(--color-success);box-shadow:0 0 0 2px var(--bg-surface)}.strip-icon.is-live .dot:after{animation:stripLivePulse 1.8s ease-out infinite;background:var(--color-success);border-radius:50%;content:"";inset:-2px;position:absolute}.strip-icon.is-live .strip-label{background:var(--color-success);border-radius:var(--radius-pill);color:var(--color-text-inverse);display:inline-block;font-weight:var(--weight-bold);letter-spacing:.04em;line-height:1.2;margin-top:2px;padding:2px var(--space-6);text-transform:uppercase}@media (prefers-reduced-motion:reduce){.strip-icon.is-live .dot:after{animation:none}}.strip-badge{align-items:center;background:var(--color-primary);border-radius:999px;box-shadow:0 0 0 2px var(--color-text-inverse);color:var(--color-text-inverse);display:flex;font-size:9px;font-variant-numeric:tabular-nums;font-weight:700;height:16px;justify-content:center;line-height:1;min-width:18px;padding:0 5px;position:absolute;right:8px;top:4px}.strip-divider{background:var(--bg-surface-3);margin:6px auto;width:70%}body.ropen .rpanel-open{display:block}.ptabs.export-toptabs{align-items:stretch;border-bottom:1px solid var(--border-default);gap:0;height:auto;margin:0 0 var(--space-20);padding:0}.ptabs.export-toptabs .ptab{align-items:center;display:inline-flex;font-size:var(--font-14);font-weight:var(--weight-semibold);gap:var(--space-6);padding:var(--space-12) var(--space-16)}.ptabs.export-toptabs .ptab i{font-size:var(--font-16)}.ptabs.export-toptabs .lib-tab-count{background:var(--bg-surface-3);border-radius:var(--radius-pill);color:var(--text-muted);font-size:var(--font-11);font-variant-numeric:tabular-nums;font-weight:var(--weight-bold);min-width:18px;padding:0 var(--space-6);text-align:center}.ptabs.export-toptabs .ptab.active .lib-tab-count{background:var(--bg-hover);color:var(--color-secondary)}.ptabs.views-toptabs{align-items:stretch;border-bottom:1px solid var(--border-default);gap:0;height:auto;margin:0 0 var(--space-16);padding:0}.ptabs.views-toptabs .ptab{align-items:center;display:inline-flex;font-size:var(--font-14);font-weight:var(--weight-semibold);gap:var(--space-6);padding:var(--space-12) var(--space-16)}.ptabs.views-toptabs .ptab i{font-size:var(--font-16)}.ptabs.views-toptabs .lib-tab-count{background:var(--bg-surface-3);border-radius:var(--radius-pill);color:var(--text-muted);font-size:var(--font-11);font-variant-numeric:tabular-nums;font-weight:var(--weight-bold);min-width:18px;padding:0 var(--space-6);text-align:center}.ptabs.views-toptabs .ptab.active .lib-tab-count{background:var(--bg-hover);color:var(--color-secondary)}.ptabs.alerts-toptabs{gap:var(--space-4);margin:0 0 var(--space-16);padding:0}.ptabs.alerts-toptabs .ptab{font-size:var(--font-13);padding:var(--space-8) var(--space-12)}.ptabs.alerts-toptabs .ptab i{font-size:var(--font-16)}.topbar{background:var(--bg-surface);border-bottom:1px solid var(--border-default);padding-top:28px;position:sticky;top:var(--topbar-merged-h);transition:padding-top .2s ease;z-index:31}.topbar.compact{padding-top:10px}.topbar.compact .pane-title{font-size:19px;margin-bottom:8px}.ptabs{align-items:flex-end;display:flex;gap:4px;height:var(--tabs-h);padding:0 32px}.ptab{border-bottom:3px solid transparent;color:var(--text-secondary);cursor:pointer;font-size:14px;font-weight:500;padding:0 20px 12px;position:relative;transition:color .15s;-webkit-user-select:none;-moz-user-select:none;user-select:none}.ptab:hover{color:var(--text-primary)}.ptab.active{background:linear-gradient(180deg,transparent 30%,rgba(var(--brand-rgb),.06) 100%);border-bottom-color:var(--color-primary);border-radius:6px 6px 0 0;color:var(--color-primary);font-weight:700}.strip-icon--hub{display:none}.strip-divider{background:var(--border-default);height:1px;margin:var(--space-8) auto var(--space-4);width:60%}.topbar>*{margin-left:auto;margin-right:auto;max-width:var(--content-max-w);width:100%}body.lcollapsed .hint{left:calc(var(--sidebar-collapsed-w) + 16px)}body.ropen .hint{right:calc(var(--rsidebar-w) + 16px)}.mobile-hamburger{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:999px;box-shadow:0 2px 8px rgba(0,0,0,.08);cursor:pointer;display:none;height:40px;justify-content:center;left:10px;position:fixed;top:10px;width:40px;z-index:260}.mobile-hamburger i{color:var(--text-primary);font-size:18px}.mobile-backdrop{background:rgba(0,0,0,.4);display:none;inset:0;position:fixed;z-index:240}body.lopen .mobile-backdrop{display:block}@media (max-width:767px){.mobile-hamburger{display:inline-flex}body.lopen .mobile-hamburger{display:none}.sidebar{box-shadow:4px 0 16px rgba(0,0,0,.15);transform:translateX(-100%);transition:transform .24s cubic-bezier(.2,0,0,1);width:280px;z-index:250}body.lopen .sidebar{transform:translateX(0)}body.lcollapsed .sidebar{width:280px}#rDrawer,.rsidebar{display:none!important}.ptabs{flex-wrap:nowrap;overflow-x:auto;padding:0 14px;scrollbar-width:thin}.ptabs::-webkit-scrollbar{height:4px}.ptab{flex-shrink:0;white-space:nowrap}}body.rm-shell{background:var(--bg-app);color:var(--text-primary);font-family:var(--font-sans);margin:0;padding-left:var(--sidebar-w);padding-right:var(--rsidebar-collapsed-w);padding-top:var(--topbar-merged-h);transition:padding .24s cubic-bezier(.2,0,0,1)}body.rm-shell.lcollapsed{padding-left:var(--sidebar-collapsed-w)}body.rm-switching.rm-shell,body.theme-switching.rm-shell{transition:none!important}@media (max-width:767px){body.rm-shell,body.rm-shell.lcollapsed{padding-left:0;padding-right:0}}
@@ -1 +1 @@
1
- .grid-2{grid-template-columns:repeat(2,1fr)}.grid-2,.grid-3{display:grid;gap:20px}.grid-3{grid-template-columns:repeat(3,1fr)}.grid-4{display:grid;gap:16px;grid-template-columns:repeat(4,1fr)}.grid-2-1{display:grid;gap:20px;grid-template-columns:2fr 1fr}.pane-title{color:var(--text-primary);font-family:var(--font-serif);font-size:32px;font-weight:700;letter-spacing:-.02em;line-height:1.1;margin:0 0 16px}.pane-sub{color:var(--text-muted);font-size:13px;line-height:1.55;margin:0 0 24px;max-width:78ch}.chart-section{margin-inline:auto;max-width:var(--content-max-w,1440px);padding:0 0 40px;width:100%}.section-label{align-items:center;color:var(--text-secondary);display:flex;font-size:11px;font-weight:700;gap:8px;letter-spacing:.12em;margin:40px 0 18px;text-transform:uppercase}body.text-large .pane-title{font-size:38px}body.text-xl .pane-title{font-size:46px}body.text-large .section-label{font-size:13px}body.text-xl .section-label{font-size:14px}.chart-card{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:10px;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;transition:box-shadow .18s,border-color .18s}.chart-card:hover{border-color:var(--color-violet-pale);box-shadow:0 6px 16px rgba(107,63,255,.1),0 1px 3px rgba(30,41,59,.04)}.chart-card-head{padding:22px 64px 14px 24px;position:relative}.chart-card-title{color:var(--text-primary);font-size:15px;font-weight:700;letter-spacing:-.01em;line-height:1.3;margin:0 0 5px}.chart-card-context{color:var(--text-muted);font-size:12px;line-height:1.55;margin:0}.chart-card-actions{position:absolute;right:12px;top:12px}.cc-export{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:6px;color:var(--text-secondary);cursor:pointer;display:flex;font-size:14px;height:28px;justify-content:center;transition:background .15s,color .15s,border-color .15s;width:28px}.cc-export:hover{color:var(--color-secondary)}.cc-export:hover,.chart-card-actions.open .cc-export{background:var(--bg-hover);border-color:var(--color-violet-pale)}.chart-card-actions.open .cc-export{color:var(--color-primary)}.cc-menu{animation:cc-menu-in .12s cubic-bezier(.2,0,0,1);background:var(--bg-surface);border:1px solid var(--border-default);border-radius:8px;box-shadow:var(--shadow-lg);display:none;min-width:200px;padding:6px 0;position:absolute;right:0;top:34px;z-index:var(--z-modal)}.chart-card-actions.open .cc-menu{display:block}.cc-menu-section{color:var(--text-muted);font-size:10px;font-weight:700;letter-spacing:.08em;padding:8px 14px 4px;text-transform:uppercase}.cc-menu-item{align-items:center;background:none;border:0;color:var(--text-secondary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:13px;gap:10px;padding:7px 14px;text-align:left;width:100%}.cc-menu-item i{color:var(--text-muted);font-size:14px}.cc-menu-item:hover{background:var(--bg-surface-3);color:var(--text-primary)}.cc-menu-item:hover i{color:var(--text-secondary)}.cc-menu-divider{background:var(--bg-surface-3);height:1px;margin:4px 0}@keyframes cc-menu-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.chart-card-body{flex:1;padding:4px 12px 8px}.chart-card-foot{align-items:center;background:var(--bg-surface-2);border-radius:0 0 10px 10px;border-top:1px solid var(--border-soft);color:var(--text-muted);display:flex;flex-wrap:wrap;font-size:11px;gap:10px;padding:12px 24px}.chart-card-foot .src{align-items:center;display:flex;gap:4px}.chart-card-foot .src b{color:var(--text-secondary);font-weight:600}.chart-card-foot .dot{background:var(--border-default);border-radius:50%;height:3px;width:3px}.chart-card-foot .updated{color:var(--text-secondary);font-weight:500}.kpi-card{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:8px;box-shadow:0 2px 8px rgba(30,41,59,.06);padding:16px 18px}.kpi-label{color:var(--text-muted);font-size:11px;font-weight:600;letter-spacing:.1em;margin:0 0 6px;text-transform:uppercase}.kpi-value{color:var(--color-primary);font-family:var(--font-serif);font-size:28px;font-weight:700;line-height:1;margin:0}.kpi-value.healthcare{color:var(--color-healthcare)}.kpi-value.discovery{color:var(--color-discovery)}.kpi-value.pharma{color:var(--color-pharma)}.kpi-value.databank{color:var(--color-databank)}.kpi-trend{align-items:center;display:inline-flex;font-size:12px;font-weight:600;gap:4px;margin-top:6px}.kpi-trend i{font-size:11px}.kpi-trend--up{color:var(--color-success)}.kpi-trend--down{color:var(--color-danger)}.kpi-foot,.kpi-trend--flat{color:var(--text-muted)}.kpi-foot{font-size:11px;margin-top:4px}.badge{border-radius:4px;display:inline-block;font-size:10px;font-weight:600;padding:2px 8px}.badge-active{background:var(--color-success-bg);color:var(--color-success)}.badge-pending{background:var(--color-warning-bg);color:var(--color-warning)}.badge-error{background:var(--color-danger-bg);color:var(--color-danger)}.badge-info{background:var(--bg-hover);color:var(--color-primary)}.badge-grey{background:var(--bg-surface-3);color:var(--text-secondary)}table.tk{border-collapse:collapse;font-size:13px;width:100%}.sr-only,table.tk>caption.sr-only{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border:0;white-space:nowrap}table.tk caption{caption-side:top;color:var(--text-primary);font-size:var(--font-13);font-weight:var(--weight-bold);padding:0 0 var(--space-8);text-align:left}table.tk th{background:var(--bg-surface-2);border-bottom:1px solid var(--border-default);color:var(--text-secondary);font-size:11px;font-weight:700;letter-spacing:.08em;padding:12px 14px;text-align:left;text-transform:uppercase}table.tk tbody th[scope=row]{background:transparent;font-size:var(--font-13);font-weight:var(--weight-bold);letter-spacing:0;text-transform:none}table.tk tbody th[scope=row],table.tk td{border-bottom:1px solid var(--border-soft);color:var(--text-primary)}table.tk td{padding:12px 14px}table.tk tbody tr:nth-child(2n) td,table.tk tbody tr:nth-child(2n) th[scope=row]{background:var(--bg-surface-2)}table.tk tbody tr:hover td,table.tk tbody tr:hover th[scope=row]{background:var(--bg-hover)}table.tk th[aria-sort]{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}table.tk th[aria-sort]:hover{color:var(--text-primary)}.tk-wrap{overflow-x:auto}.tk-num{font-variant-numeric:tabular-nums;text-align:right}.nav-group{display:flex;flex-direction:column}.nav-group-header{align-items:center;background:none;border:0;color:var(--text-muted);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:11px;font-weight:700;gap:8px;letter-spacing:.08em;padding:8px 12px;text-align:left;text-transform:uppercase;transition:color var(--duration-fast) var(--ease-out);width:100%}.nav-group-header:hover{color:var(--text-secondary)}.nav-group-header .caret{display:inline-flex;margin-left:auto;transition:transform var(--duration-fast) var(--ease-out)}.nav-group[data-open=true]>.nav-group-header{color:var(--color-primary)}.nav-group[data-open=true]>.nav-group-header .caret{transform:rotate(180deg)}.nav-group-items{display:flex;flex-direction:column;gap:2px;padding:2px 0 8px}.cc-export:focus-visible,.cc-menu-item:focus-visible,.chart-card :focus-visible,.nav-group-header:focus-visible,table.tk th[aria-sort]:focus-visible{box-shadow:var(--shadow-focus);outline:2px solid var(--color-secondary);outline-offset:2px}.cc-menu-item,.nav-group-header{min-height:40px}body.text-large .kpi-label{font-size:13px}body.text-large .kpi-value{font-size:34px}body.text-large .kpi-trend{font-size:14px}body.text-large .kpi-foot{font-size:13px}body.text-large .chart-card-title{font-size:17px}body.text-large .chart-card-context{font-size:14px}body.text-large .chart-card-foot{font-size:12px}body.text-large .badge{font-size:12px;padding:3px 9px}body.text-large table.tk{font-size:15px}body.text-large table.tk th{font-size:13px}body.text-xl .kpi-label{font-size:14px}body.text-xl .kpi-value{font-size:40px}body.text-xl .kpi-trend{font-size:16px}body.text-xl .kpi-foot{font-size:14px}body.text-xl .chart-card-title{font-size:20px}body.text-xl .chart-card-context{font-size:16px}body.text-xl .chart-card-foot{font-size:14px}body.text-xl .badge{font-size:14px;padding:3px 10px}body.text-xl table.tk{font-size:16px}body.text-xl table.tk th{font-size:14px}body.high-contrast .cc-export,body.high-contrast .cc-menu,body.high-contrast .chart-card,body.high-contrast .kpi-card{border-width:2px}body.high-contrast .chart-card:hover{border-color:var(--border-default)}body.theme-switching,body.theme-switching *,body.theme-switching :after,body.theme-switching :before{transition:none!important}body.high-contrast *{box-shadow:none!important}
1
+ .grid-2{grid-template-columns:repeat(2,1fr)}.grid-2,.grid-3{display:grid;gap:20px}.grid-3{grid-template-columns:repeat(3,1fr)}.grid-4{display:grid;gap:16px;grid-template-columns:repeat(4,1fr)}.grid-2-1{display:grid;gap:20px;grid-template-columns:2fr 1fr}.pane-title{color:var(--text-primary);font-family:var(--font-serif);font-size:32px;font-weight:700;letter-spacing:-.02em;line-height:1.1;margin:0 0 16px}.pane-sub{color:var(--text-muted);font-size:13px;line-height:1.55;margin:0 0 24px;max-width:78ch}.chart-section{margin-inline:auto;max-width:var(--content-max-w,1440px);padding:0 0 40px;width:100%}.section-label{align-items:center;color:var(--text-secondary);display:flex;font-size:11px;font-weight:700;gap:8px;letter-spacing:.12em;margin:40px 0 18px;text-transform:uppercase}body.text-large .pane-title{font-size:38px}body.text-xl .pane-title{font-size:46px}body.text-large .section-label{font-size:13px}body.text-xl .section-label{font-size:14px}.chart-card{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:10px;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;transition:box-shadow .18s,border-color .18s}.chart-card:hover{border-color:var(--color-violet-pale);box-shadow:0 6px 16px rgba(107,63,255,.1),0 1px 3px rgba(30,41,59,.04)}.chart-card-head{padding:22px 64px 14px 24px;position:relative}.chart-card-title{color:var(--text-primary);font-size:15px;font-weight:700;letter-spacing:-.01em;line-height:1.3;margin:0 0 5px}.chart-card-context{color:var(--text-muted);font-size:12px;line-height:1.55;margin:0}.chart-card-actions{position:absolute;right:12px;top:12px}.cc-export{align-items:center;background:var(--bg-surface);border:1px solid var(--border-default);border-radius:6px;color:var(--text-secondary);cursor:pointer;display:flex;font-size:14px;height:28px;justify-content:center;transition:background .15s,color .15s,border-color .15s;width:28px}.cc-export:hover{color:var(--color-secondary)}.cc-export:hover,.chart-card-actions.open .cc-export{background:var(--bg-hover);border-color:var(--color-violet-pale)}.chart-card-actions.open .cc-export{color:var(--color-primary)}.cc-menu{animation:cc-menu-in .12s cubic-bezier(.2,0,0,1);background:var(--bg-surface);border:1px solid var(--border-default);border-radius:8px;box-shadow:var(--shadow-lg);display:none;min-width:200px;padding:6px 0;position:absolute;right:0;top:34px;z-index:var(--z-modal)}.chart-card-actions.open .cc-menu{display:block}.cc-menu-section{color:var(--text-muted);font-size:10px;font-weight:700;letter-spacing:.08em;padding:8px 14px 4px;text-transform:uppercase}.cc-menu-item{align-items:center;background:none;border:0;color:var(--text-secondary);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:13px;gap:10px;padding:7px 14px;text-align:left;width:100%}.cc-menu-item i{color:var(--text-muted);font-size:14px}.cc-menu-item:hover{background:var(--bg-surface-3);color:var(--text-primary)}.cc-menu-item:hover i{color:var(--text-secondary)}.cc-menu-divider{background:var(--bg-surface-3);height:1px;margin:4px 0}@keyframes cc-menu-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.chart-card-body{flex:1;padding:4px 12px 8px}.chart-card-foot{align-items:center;background:var(--bg-surface-2);border-radius:0 0 10px 10px;border-top:1px solid var(--border-soft);color:var(--text-muted);display:flex;flex-wrap:wrap;font-size:11px;gap:10px;padding:12px 24px}.chart-card-foot .src{align-items:center;display:flex;gap:4px}.chart-card-foot .src b{color:var(--text-secondary);font-weight:600}.chart-card-foot .dot{background:var(--border-default);border-radius:50%;height:3px;width:3px}.chart-card-foot .updated{color:var(--text-secondary);font-weight:500}.kpi-card{background:var(--bg-surface);border:1px solid var(--border-default);border-radius:8px;box-shadow:0 2px 8px rgba(30,41,59,.06);padding:16px 18px}.kpi-label{color:var(--text-muted);font-size:11px;font-weight:600;letter-spacing:.1em;margin:0 0 6px;text-transform:uppercase}.kpi-value{color:var(--color-primary);font-family:var(--font-serif);font-size:28px;font-weight:700;line-height:1;margin:0}.kpi-value.healthcare{color:var(--color-healthcare)}.kpi-value.discovery{color:var(--color-discovery)}.kpi-value.pharma{color:var(--color-pharma)}.kpi-value.databank{color:var(--color-databank)}.kpi-trend{align-items:center;display:inline-flex;font-size:12px;font-weight:600;gap:4px;margin-top:6px}.kpi-trend i{font-size:11px}.kpi-trend--up{color:var(--color-success)}.kpi-trend--down{color:var(--color-danger)}.kpi-foot,.kpi-trend--flat{color:var(--text-muted)}.kpi-foot{font-size:11px;margin-top:4px}.badge{border-radius:4px;display:inline-block;font-size:10px;font-weight:600;padding:2px 8px}.badge-active{background:var(--color-success-bg);color:var(--color-success)}.badge-pending{background:var(--color-warning-bg);color:var(--color-warning)}.badge-error{background:var(--color-danger-bg);color:var(--color-danger)}.badge-info{background:var(--bg-hover);color:var(--color-primary)}.badge-grey{background:var(--bg-surface-3);color:var(--text-secondary)}table.tk{border-collapse:collapse;font-size:13px;width:100%}.sr-only,table.tk>caption.sr-only{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border:0;white-space:nowrap}table.tk caption{caption-side:top;color:var(--text-primary);font-size:var(--font-13);font-weight:var(--weight-bold);padding:0 0 var(--space-8);text-align:left}table.tk th{background:var(--bg-surface-2);border-bottom:1px solid var(--border-default);color:var(--text-secondary);font-size:11px;font-weight:700;letter-spacing:.08em;padding:12px 14px;text-align:left;text-transform:uppercase}table.tk tbody th[scope=row]{background:transparent;font-size:var(--font-13);font-weight:var(--weight-bold);letter-spacing:0;text-transform:none}table.tk tbody th[scope=row],table.tk td{border-bottom:1px solid var(--border-soft);color:var(--text-primary)}table.tk td{padding:12px 14px}table.tk tbody tr:nth-child(2n) td,table.tk tbody tr:nth-child(2n) th[scope=row]{background:var(--bg-surface-2)}table.tk tbody tr:hover td,table.tk tbody tr:hover th[scope=row]{background:var(--bg-hover)}table.tk th[aria-sort]{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}table.tk th[aria-sort]:hover{color:var(--text-primary)}.tk-wrap{overflow-x:auto}.tk-num{font-variant-numeric:tabular-nums;text-align:right}.nav-group{display:flex;flex-direction:column}.nav-group-header{align-items:center;background:none;border:0;color:var(--text-muted);cursor:pointer;display:flex;font-family:var(--font-sans);font-size:11px;font-weight:700;gap:8px;letter-spacing:.08em;padding:8px 12px;text-align:left;text-transform:uppercase;transition:color var(--duration-fast) var(--ease-out);width:100%}.nav-group-header:hover{color:var(--text-secondary)}.nav-group-header .caret{display:inline-flex;margin-left:auto;transition:transform var(--duration-fast) var(--ease-out)}.nav-group[data-open=true]>.nav-group-header{color:var(--color-primary)}.nav-group[data-open=true]>.nav-group-header .caret{transform:rotate(180deg)}.nav-group-items{display:flex;flex-direction:column;gap:2px;padding:2px 0 8px}.cc-export:focus-visible,.cc-menu-item:focus-visible,.chart-card :focus-visible,.nav-group-header:focus-visible,table.tk th[aria-sort]:focus-visible{box-shadow:var(--shadow-focus);outline:2px solid var(--color-secondary);outline-offset:2px}.cc-menu-item,.nav-group-header{min-height:40px}body.text-large .kpi-label{font-size:13px}body.text-large .kpi-value{font-size:34px}body.text-large .kpi-trend{font-size:14px}body.text-large .kpi-foot{font-size:13px}body.text-large .chart-card-title{font-size:17px}body.text-large .chart-card-context{font-size:14px}body.text-large .chart-card-foot{font-size:12px}body.text-large .badge{font-size:12px;padding:3px 9px}body.text-large table.tk{font-size:15px}body.text-large table.tk th{font-size:13px}body.text-xl .kpi-label{font-size:14px}body.text-xl .kpi-value{font-size:40px}body.text-xl .kpi-trend{font-size:16px}body.text-xl .kpi-foot{font-size:14px}body.text-xl .chart-card-title{font-size:20px}body.text-xl .chart-card-context{font-size:16px}body.text-xl .chart-card-foot{font-size:14px}body.text-xl .badge{font-size:14px;padding:3px 10px}body.text-xl table.tk{font-size:16px}body.text-xl table.tk th{font-size:14px}body.high-contrast .cc-export,body.high-contrast .cc-menu,body.high-contrast .chart-card,body.high-contrast .kpi-card{border-width:2px}body.high-contrast .chart-card:hover{border-color:var(--border-default)}body.rm-switching,body.rm-switching *,body.rm-switching :after,body.rm-switching :before,body.theme-switching,body.theme-switching *,body.theme-switching :after,body.theme-switching :before{transition:none!important}body.high-contrast *{box-shadow:none!important}
@@ -1 +1 @@
1
- :root{--sidebar-w:220px;--sidebar-collapsed-w:56px;--rsidebar-w:280px;--rsidebar-collapsed-w:76px;--topbar-h:56px;--tabs-h:46px;--analysis-header-h:124px;--sourcebar-h:38px;--filterbar-h:56px;--color-primary:#2100ad;--color-primary-hover:#1a0088;--color-secondary:#6b3fff;--color-accent-soft:#b485ff;--color-violet-pale:#d6ccff;--color-healthcare:#4164ee;--color-discovery:#07816f;--color-pharma:#f7520e;--color-databank:#e04380;--color-healthcare-active:#3451d4;--color-discovery-active:#066b5c;--color-pharma-active:#d9470c;--color-databank-active:#c73a6f;--color-pharma-soft:#ff7942;--healthcare-rgb:65,100,238;--discovery-rgb:7,129,111;--pharma-rgb:247,82,14;--databank-rgb:224,67,128;--surface-rgb:255,255,255;--shadow-rgb:30,41,59;--color-accent-teal:#00d4a8;--color-notification:#e59;--color-text-inverse:#fff;--color-success:#16a34a;--color-success-bg:#dcfce7;--color-warning:#d97706;--color-warning-bg:#fef3c7;--color-danger:#dc2626;--color-danger-bg:#fee2e2;--color-info:#4164ee;--color-info-bg:#dbeafe;--bg-app:#eef1f5;--bg-surface:#fff;--bg-surface-2:#fbfbfd;--bg-surface-3:#f5f5f9;--bg-hover:#eef1fe;--border-default:#dbdef0;--border-soft:#f0f1f7;--text-primary:#1e293b;--text-secondary:#525670;--text-muted:#878ca6;--brand-rgb:33,0,173;--chart-cat-1:#2100ad;--chart-cat-2:#00a38e;--chart-cat-3:#e67e22;--chart-cat-4:#d63384;--chart-cat-5:#4164ee;--chart-cat-6:#16a34a;--space-2:2px;--space-4:4px;--space-6:6px;--space-8:8px;--space-12:12px;--space-16:16px;--space-20:20px;--space-24:24px;--space-32:32px;--space-40:40px;--space-48:48px;--radius-sm:4px;--radius-md:6px;--radius-lg:8px;--radius-xl:10px;--radius-2xl:12px;--radius-pill:999px;--font-10:10px;--font-11:11px;--font-12:12px;--font-13:13px;--font-14:14px;--font-16:16px;--font-18:18px;--font-22:22px;--font-24:24px;--font-32:32px;--weight-normal:400;--weight-medium:500;--weight-semibold:600;--weight-bold:700;--font-serif:"Caudex",Georgia,serif;--font-sans:"Figtree",system-ui,sans-serif;--font-mono:"SF Mono",Menlo,monospace;--shadow-xs:0 1px 2px rgba(30,41,59,.04);--shadow-sm:0 2px 6px rgba(30,41,59,.06),0 1px 2px rgba(30,41,59,.03);--shadow-md:0 4px 12px rgba(30,41,59,.06),0 1px 3px rgba(30,41,59,.04);--shadow-lg:0 8px 24px rgba(30,41,59,.12);--shadow-focus:0 0 0 3px rgba(107,63,255,.14);--duration-instant:100ms;--duration-fast:150ms;--duration-base:240ms;--duration-slow:400ms;--duration-slowest:600ms;--ease-out:cubic-bezier(.2,0,0,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--ease-spring:cubic-bezier(.5,1.6,.4,1);--z-base:1;--z-sticky:20;--z-header:30;--z-drawer:40;--z-modal:50;--z-toast:60;--z-help:70}body.night{--color-primary:#9b7cff;--color-secondary:#b485ff;--color-accent-soft:#d6ccff;--color-healthcare:#7aa0ff;--color-discovery:#2ecc71;--color-pharma:#ff8c00;--color-databank:#f472b6;--color-healthcare-active:#4164ee;--color-discovery-active:#07816f;--color-pharma-active:#f7520e;--color-databank-active:#e04380;--healthcare-rgb:122,160,255;--discovery-rgb:46,204,113;--pharma-rgb:255,140,0;--databank-rgb:244,114,182;--surface-rgb:26,26,36;--shadow-rgb:0,0,0;--bg-app:#0e0e14;--bg-surface:#1a1a24;--bg-surface-2:#232330;--bg-surface-3:#2a2a38;--bg-hover:#2d2a42;--border-default:#45455c;--border-soft:#32323f;--text-primary:#f5f6fa;--text-secondary:#cfd3e5;--text-muted:#8a90a8;--brand-rgb:155,124,255;--color-success:#6fe8c5;--color-success-bg:rgba(0,212,168,.18);--color-warning:#fbbf24;--color-warning-bg:rgba(245,158,11,.18);--color-danger:#fca5a5;--color-danger-bg:rgba(239,68,68,.18);--color-info:#7aa0ff;--color-info-bg:rgba(65,100,238,.18);--chart-cat-1:#9b7cff;--chart-cat-2:#6fe8c5;--chart-cat-3:#fbbf24;--chart-cat-4:#f472b6;--chart-cat-5:#7b9ef5;--chart-cat-6:#bef264;background:var(--bg-app);color:var(--text-primary)}body.high-contrast:not(.night){--text-primary:#000;--text-secondary:#1a1a24;--text-muted:#2f2f40;--border-default:#000;--border-soft:#525670;--bg-app:#fff;--bg-surface:#fff;--bg-surface-2:#fff;--bg-surface-3:#f0f1f7;--bg-hover:#ffe56b;--color-primary:#1100a0;--color-primary-hover:#0a0070;--color-secondary:#5028d8;--color-success:#006b2c;--color-warning:#8c4500;--color-danger:#a60000;--color-info:#0040c8}body.night.high-contrast{--text-primary:#fff;--text-secondary:#f5f6fa;--text-muted:#cfd3e5;--border-default:#fff;--border-soft:#b8bcd0;--bg-app:#000;--bg-surface:#000;--bg-surface-2:#000;--bg-surface-3:#000;--bg-hover:#ffe56b;--color-primary:#d6ccff;--color-primary-hover:#fff;--color-secondary:#e5daff;--color-accent-soft:#fff;--color-success:#6fe8c5;--color-warning:#fbbf24;--color-danger:#fca5a5;--color-info:#7aa0ff;--brand-rgb:214,204,255}body.reduce-motion *,body.reduce-motion :after,body.reduce-motion :before{animation:none!important;scroll-behavior:auto!important;transition:none!important}:root{--r-color-primary-50:#f0f4ff;--r-color-primary-100:#dce5fd;--r-color-primary-200:#b9cbfb;--r-color-primary-300:#96b0f9;--r-color-primary-400:#7395f5;--r-color-primary-500:#5a7ef2;--r-color-primary-600:var(--color-healthcare);--r-color-primary-700:#3451d4;--r-color-primary-800:#283fb0;--r-color-primary-900:#1c2d8a;--r-color-gray-50:#f9fafb;--r-color-gray-100:#f3f4f6;--r-color-gray-200:#e5e7eb;--r-color-gray-300:#d1d5db;--r-color-gray-400:#9ca3af;--r-color-gray-500:#6b7280;--r-color-gray-600:#4b5563;--r-color-gray-700:#374151;--r-color-gray-800:#1f2937;--r-color-gray-900:#111827;--r-color-gray-950:#030712;--r-color-success-500:var(--color-success);--r-color-success-600:#16a34a;--r-color-warning-500:#f59e0b;--r-color-warning-600:var(--color-warning);--r-color-danger-500:#ef4444;--r-color-danger-600:var(--color-danger);--r-color-white:#fff;--r-color-black:#000;--r-font-family:var(--font-sans);--r-font-mono:var(--font-mono);--r-text-xs:0.75rem;--r-text-sm:0.875rem;--r-text-base:1rem;--r-text-lg:1.125rem;--r-text-xl:1.25rem;--r-text-2xl:1.5rem;--r-text-3xl:1.875rem;--r-text-4xl:2.25rem;--r-font-weight-normal:var(--weight-normal);--r-font-weight-medium:var(--weight-medium);--r-font-weight-semibold:var(--weight-semibold);--r-font-weight-bold:var(--weight-bold);--r-leading-tight:1.25;--r-leading-normal:1.5;--r-leading-relaxed:1.75;--r-space-0:0;--r-space-1:var(--space-4);--r-space-2:var(--space-8);--r-space-3:var(--space-12);--r-space-4:var(--space-16);--r-space-5:var(--space-20);--r-space-6:var(--space-24);--r-space-8:var(--space-32);--r-space-10:var(--space-40);--r-space-12:var(--space-48);--r-radius-none:0;--r-radius-sm:var(--radius-sm);--r-radius-md:var(--radius-md);--r-radius-lg:var(--radius-lg);--r-radius-xl:0.75rem;--r-radius-2xl:1rem;--r-radius-full:var(--radius-pill);--r-shadow-xs:0 1px 2px 0 rgba(0,0,0,.05);--r-shadow-sm:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--r-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--r-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--r-shadow-xl:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--r-transition-fast:var(--duration-fast) var(--ease-in-out);--r-transition-base:200ms var(--ease-in-out);--r-transition-slow:var(--duration-slow) var(--ease-in-out)}
1
+ :root{--sidebar-w:220px;--sidebar-collapsed-w:56px;--rsidebar-w:280px;--rsidebar-collapsed-w:76px;--topbar-h:56px;--topbar-merged-h:0px;--tabs-h:46px;--analysis-header-h:124px;--sourcebar-h:38px;--filterbar-h:56px;--color-primary:#2100ad;--color-primary-hover:#1a0088;--color-secondary:#6b3fff;--color-accent-soft:#b485ff;--color-violet-pale:#d6ccff;--color-healthcare:#4164ee;--color-discovery:#07816f;--color-pharma:#f7520e;--color-databank:#e04380;--color-healthcare-active:#3451d4;--color-discovery-active:#066b5c;--color-pharma-active:#d9470c;--color-databank-active:#c73a6f;--color-pharma-soft:#ff7942;--healthcare-rgb:65,100,238;--discovery-rgb:7,129,111;--pharma-rgb:247,82,14;--databank-rgb:224,67,128;--surface-rgb:255,255,255;--shadow-rgb:30,41,59;--color-accent-teal:#00d4a8;--color-notification:#e59;--color-text-inverse:#fff;--color-success:#16a34a;--color-success-bg:#dcfce7;--color-warning:#d97706;--color-warning-bg:#fef3c7;--color-danger:#dc2626;--color-danger-bg:#fee2e2;--color-info:#4164ee;--color-info-bg:#dbeafe;--bg-app:#eef1f5;--bg-surface:#fff;--bg-surface-2:#fbfbfd;--bg-surface-3:#f5f5f9;--bg-hover:#eef1fe;--border-default:#dbdef0;--border-soft:#f0f1f7;--text-primary:#1e293b;--text-secondary:#525670;--text-muted:#878ca6;--brand-rgb:33,0,173;--chart-cat-1:#2100ad;--chart-cat-2:#00a38e;--chart-cat-3:#e67e22;--chart-cat-4:#d63384;--chart-cat-5:#4164ee;--chart-cat-6:#16a34a;--space-2:2px;--space-4:4px;--space-6:6px;--space-8:8px;--space-12:12px;--space-16:16px;--space-20:20px;--space-24:24px;--space-32:32px;--space-40:40px;--space-48:48px;--radius-sm:4px;--radius-md:6px;--radius-lg:8px;--radius-xl:10px;--radius-2xl:12px;--radius-pill:999px;--font-10:10px;--font-11:11px;--font-12:12px;--font-13:13px;--font-14:14px;--font-16:16px;--font-18:18px;--font-22:22px;--font-24:24px;--font-32:32px;--weight-normal:400;--weight-medium:500;--weight-semibold:600;--weight-bold:700;--font-serif:"Caudex",Georgia,serif;--font-sans:"Figtree",system-ui,sans-serif;--font-mono:"SF Mono",Menlo,monospace;--shadow-xs:0 1px 2px rgba(30,41,59,.04);--shadow-sm:0 2px 6px rgba(30,41,59,.06),0 1px 2px rgba(30,41,59,.03);--shadow-md:0 4px 12px rgba(30,41,59,.06),0 1px 3px rgba(30,41,59,.04);--shadow-lg:0 8px 24px rgba(30,41,59,.12);--shadow-focus:0 0 0 3px rgba(107,63,255,.14);--duration-instant:100ms;--duration-fast:150ms;--duration-base:240ms;--duration-slow:400ms;--duration-slowest:600ms;--ease-out:cubic-bezier(.2,0,0,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--ease-spring:cubic-bezier(.5,1.6,.4,1);--z-base:1;--z-sticky:20;--z-header:30;--z-drawer:40;--z-modal:50;--z-toast:60;--z-help:70}body.night{--color-primary:#9b7cff;--color-secondary:#b485ff;--color-accent-soft:#d6ccff;--color-healthcare:#7aa0ff;--color-discovery:#2ecc71;--color-pharma:#ff8c00;--color-databank:#f472b6;--color-healthcare-active:#4164ee;--color-discovery-active:#07816f;--color-pharma-active:#f7520e;--color-databank-active:#e04380;--healthcare-rgb:122,160,255;--discovery-rgb:46,204,113;--pharma-rgb:255,140,0;--databank-rgb:244,114,182;--surface-rgb:26,26,36;--shadow-rgb:0,0,0;--bg-app:#0e0e14;--bg-surface:#1a1a24;--bg-surface-2:#232330;--bg-surface-3:#2a2a38;--bg-hover:#2d2a42;--border-default:#45455c;--border-soft:#32323f;--text-primary:#f5f6fa;--text-secondary:#cfd3e5;--text-muted:#8a90a8;--brand-rgb:155,124,255;--color-success:#6fe8c5;--color-success-bg:rgba(0,212,168,.18);--color-warning:#fbbf24;--color-warning-bg:rgba(245,158,11,.18);--color-danger:#fca5a5;--color-danger-bg:rgba(239,68,68,.18);--color-info:#7aa0ff;--color-info-bg:rgba(65,100,238,.18);--chart-cat-1:#9b7cff;--chart-cat-2:#6fe8c5;--chart-cat-3:#fbbf24;--chart-cat-4:#f472b6;--chart-cat-5:#7b9ef5;--chart-cat-6:#bef264;background:var(--bg-app);color:var(--text-primary)}body.high-contrast:not(.night){--text-primary:#000;--text-secondary:#1a1a24;--text-muted:#2f2f40;--border-default:#000;--border-soft:#525670;--bg-app:#fff;--bg-surface:#fff;--bg-surface-2:#fff;--bg-surface-3:#f0f1f7;--bg-hover:#ffe56b;--color-primary:#1100a0;--color-primary-hover:#0a0070;--color-secondary:#5028d8;--color-success:#006b2c;--color-warning:#8c4500;--color-danger:#a60000;--color-info:#0040c8}body.night.high-contrast{--text-primary:#fff;--text-secondary:#f5f6fa;--text-muted:#cfd3e5;--border-default:#fff;--border-soft:#b8bcd0;--bg-app:#000;--bg-surface:#000;--bg-surface-2:#000;--bg-surface-3:#000;--bg-hover:#ffe56b;--color-primary:#d6ccff;--color-primary-hover:#fff;--color-secondary:#e5daff;--color-accent-soft:#fff;--color-success:#6fe8c5;--color-warning:#fbbf24;--color-danger:#fca5a5;--color-info:#7aa0ff;--brand-rgb:214,204,255}body.reduce-motion *,body.reduce-motion :after,body.reduce-motion :before{animation:none!important;scroll-behavior:auto!important;transition:none!important}:root{--r-color-primary-50:#f0f4ff;--r-color-primary-100:#dce5fd;--r-color-primary-200:#b9cbfb;--r-color-primary-300:#96b0f9;--r-color-primary-400:#7395f5;--r-color-primary-500:#5a7ef2;--r-color-primary-600:var(--color-healthcare);--r-color-primary-700:#3451d4;--r-color-primary-800:#283fb0;--r-color-primary-900:#1c2d8a;--r-color-gray-50:#f9fafb;--r-color-gray-100:#f3f4f6;--r-color-gray-200:#e5e7eb;--r-color-gray-300:#d1d5db;--r-color-gray-400:#9ca3af;--r-color-gray-500:#6b7280;--r-color-gray-600:#4b5563;--r-color-gray-700:#374151;--r-color-gray-800:#1f2937;--r-color-gray-900:#111827;--r-color-gray-950:#030712;--r-color-success-500:var(--color-success);--r-color-success-600:#16a34a;--r-color-warning-500:#f59e0b;--r-color-warning-600:var(--color-warning);--r-color-danger-500:#ef4444;--r-color-danger-600:var(--color-danger);--r-color-white:#fff;--r-color-black:#000;--r-font-family:var(--font-sans);--r-font-mono:var(--font-mono);--r-text-xs:0.75rem;--r-text-sm:0.875rem;--r-text-base:1rem;--r-text-lg:1.125rem;--r-text-xl:1.25rem;--r-text-2xl:1.5rem;--r-text-3xl:1.875rem;--r-text-4xl:2.25rem;--r-font-weight-normal:var(--weight-normal);--r-font-weight-medium:var(--weight-medium);--r-font-weight-semibold:var(--weight-semibold);--r-font-weight-bold:var(--weight-bold);--r-leading-tight:1.25;--r-leading-normal:1.5;--r-leading-relaxed:1.75;--r-space-0:0;--r-space-1:var(--space-4);--r-space-2:var(--space-8);--r-space-3:var(--space-12);--r-space-4:var(--space-16);--r-space-5:var(--space-20);--r-space-6:var(--space-24);--r-space-8:var(--space-32);--r-space-10:var(--space-40);--r-space-12:var(--space-48);--r-radius-none:0;--r-radius-sm:var(--radius-sm);--r-radius-md:var(--radius-md);--r-radius-lg:var(--radius-lg);--r-radius-xl:0.75rem;--r-radius-2xl:1rem;--r-radius-full:var(--radius-pill);--r-shadow-xs:0 1px 2px 0 rgba(0,0,0,.05);--r-shadow-sm:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--r-shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--r-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--r-shadow-xl:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--r-transition-fast:var(--duration-fast) var(--ease-in-out);--r-transition-base:200ms var(--ease-in-out);--r-transition-slow:var(--duration-slow) var(--ease-in-out)}
@@ -0,0 +1,254 @@
1
+ <script setup lang="ts">
2
+ export interface AiChatMessage {
3
+ id: string
4
+ role: 'user' | 'assistant'
5
+ content: string
6
+ timestamp?: Date
7
+ }
8
+
9
+ export interface AiChatSuggestion {
10
+ icon?: string
11
+ label: string
12
+ message: string
13
+ }
14
+
15
+ export interface RmAiChatProps {
16
+ /** Whether the chat drawer is open */
17
+ modelValue: boolean
18
+ /** Chat title displayed in the header */
19
+ title?: string
20
+ /** Emoji or text icon for the header */
21
+ icon?: string
22
+ /** Input placeholder text */
23
+ placeholder?: string
24
+ /** Suggestion buttons displayed on empty state */
25
+ suggestions?: AiChatSuggestion[]
26
+ /** Chat messages */
27
+ messages?: AiChatMessage[]
28
+ /** Whether the assistant is currently responding */
29
+ loading?: boolean
30
+ /** Loading indicator text */
31
+ loadingText?: string
32
+ /** Welcome message displayed when no messages */
33
+ welcomeMessage?: string
34
+ /** Drawer size */
35
+ drawerSize?: 'sm' | 'md' | 'lg' | 'xl'
36
+ /** Show the floating trigger button */
37
+ showTrigger?: boolean
38
+ }
39
+
40
+ const props = withDefaults(defineProps<RmAiChatProps>(), {
41
+ title: 'AI Assistant',
42
+ icon: '',
43
+ placeholder: 'Type a message...',
44
+ suggestions: () => [],
45
+ messages: () => [],
46
+ loading: false,
47
+ loadingText: 'Thinking...',
48
+ welcomeMessage: 'Hello! How can I help you today?',
49
+ drawerSize: 'md',
50
+ showTrigger: true,
51
+ })
52
+
53
+ const emit = defineEmits<{
54
+ 'update:modelValue': [value: boolean]
55
+ 'send': [message: string]
56
+ 'clear': []
57
+ }>()
58
+
59
+ const inputValue = ref('')
60
+ const messagesContainer = ref<HTMLElement | null>(null)
61
+
62
+ function open() {
63
+ emit('update:modelValue', true)
64
+ }
65
+
66
+ function close() {
67
+ emit('update:modelValue', false)
68
+ }
69
+
70
+ function toggle() {
71
+ emit('update:modelValue', !props.modelValue)
72
+ }
73
+
74
+ function handleSubmit() {
75
+ if (!inputValue.value.trim() || props.loading) return
76
+ const message = inputValue.value
77
+ inputValue.value = ''
78
+ emit('send', message)
79
+ scrollToBottom()
80
+ }
81
+
82
+ function handleSuggestion(suggestion: AiChatSuggestion) {
83
+ emit('send', suggestion.message)
84
+ scrollToBottom()
85
+ }
86
+
87
+ function handleClear() {
88
+ emit('clear')
89
+ }
90
+
91
+ function scrollToBottom() {
92
+ nextTick(() => {
93
+ if (messagesContainer.value) {
94
+ messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight
95
+ }
96
+ })
97
+ }
98
+
99
+ // Scroll to bottom when drawer opens or new messages arrive
100
+ watch(() => props.modelValue, (open) => {
101
+ if (open) scrollToBottom()
102
+ })
103
+
104
+ watch(() => props.messages.length, () => {
105
+ scrollToBottom()
106
+ })
107
+
108
+ // Simple markdown renderer
109
+ function renderMarkdown(text: string): string {
110
+ if (!text) return ''
111
+ return text
112
+ .replace(/&/g, '&amp;')
113
+ .replace(/</g, '&lt;')
114
+ .replace(/>/g, '&gt;')
115
+ .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
116
+ .replace(/\*(.+?)\*/g, '<em>$1</em>')
117
+ .replace(/^(\d+)\.\s+(.+)$/gm, '<li class="rm-ai-chat-numbered">$2</li>')
118
+ .replace(/^[-*]\s+(.+)$/gm, '<li class="rm-ai-chat-bullet">$1</li>')
119
+ .replace(/((?:<li class="rm-ai-chat-numbered">.*?<\/li>)+)/g, '<ol>$1</ol>')
120
+ .replace(/((?:<li class="rm-ai-chat-bullet">.*?<\/li>)+)/g, '<ul>$1</ul>')
121
+ .replace(/\n/g, '<br>')
122
+ }
123
+
124
+ // Close on Escape
125
+ onMounted(() => {
126
+ const handleEscape = (e: KeyboardEvent) => {
127
+ if (e.key === 'Escape' && props.modelValue) {
128
+ close()
129
+ }
130
+ }
131
+ document.addEventListener('keydown', handleEscape)
132
+ onUnmounted(() => {
133
+ document.removeEventListener('keydown', handleEscape)
134
+ })
135
+ })
136
+
137
+ defineExpose({ open, close, toggle, scrollToBottom })
138
+ </script>
139
+
140
+ <template>
141
+ <!-- Floating trigger button -->
142
+ <button
143
+ v-if="showTrigger"
144
+ class="rm-ai-chat-trigger"
145
+ :class="{ 'rm-ai-chat-trigger--open': modelValue }"
146
+ @click="toggle"
147
+ >
148
+ <slot name="trigger">
149
+ <RmIcon name="Robot" :size="24" weight="duotone" />
150
+ </slot>
151
+ </button>
152
+
153
+ <!-- Chat Drawer -->
154
+ <RmDrawer
155
+ :model-value="modelValue"
156
+ :size="drawerSize"
157
+ side="right"
158
+ @update:model-value="emit('update:modelValue', $event)"
159
+ >
160
+ <template #header>
161
+ <div class="rm-ai-chat-header">
162
+ <span v-if="icon" class="rm-ai-chat-header__icon">{{ icon }}</span>
163
+ <RmIcon v-else name="Robot" :size="22" weight="duotone" class="rm-ai-chat-header__robot" />
164
+ <h3 class="rm-ai-chat-header__title">{{ title }}</h3>
165
+ </div>
166
+ <button class="rm-ai-chat-header__clear" @click="handleClear" title="Clear chat">
167
+ <RmIcon name="Trash" :size="16" />
168
+ </button>
169
+ </template>
170
+
171
+ <div class="rm-ai-chat-body">
172
+ <div ref="messagesContainer" class="rm-ai-chat-messages">
173
+ <!-- Welcome state -->
174
+ <div v-if="messages.length === 0" class="rm-ai-chat-welcome">
175
+ <div class="rm-ai-chat-message rm-ai-chat-message--assistant rm-ai-chat-message--welcome">
176
+ <div class="rm-ai-chat-avatar rm-ai-chat-avatar--assistant">
177
+ <RmIcon name="Robot" :size="18" weight="duotone" />
178
+ </div>
179
+ <div class="rm-ai-chat-bubble rm-ai-chat-bubble--assistant rm-ai-chat-bubble--welcome">
180
+ <slot name="welcome">
181
+ {{ welcomeMessage }}
182
+ </slot>
183
+ </div>
184
+ </div>
185
+
186
+ <div v-if="suggestions.length" class="rm-ai-chat-suggestions">
187
+ <p class="rm-ai-chat-suggestions__hint">Try asking:</p>
188
+ <button
189
+ v-for="(suggestion, i) in suggestions"
190
+ :key="i"
191
+ class="rm-ai-chat-suggestion"
192
+ @click="handleSuggestion(suggestion)"
193
+ >
194
+ <span v-if="suggestion.icon" class="rm-ai-chat-suggestion__icon">{{ suggestion.icon }}</span>
195
+ {{ suggestion.label }}
196
+ </button>
197
+ </div>
198
+ </div>
199
+
200
+ <!-- Messages -->
201
+ <div
202
+ v-for="message in messages"
203
+ :key="message.id"
204
+ class="rm-ai-chat-message"
205
+ :class="`rm-ai-chat-message--${message.role}`"
206
+ >
207
+ <div class="rm-ai-chat-avatar" :class="`rm-ai-chat-avatar--${message.role}`">
208
+ <RmIcon v-if="message.role === 'assistant'" name="Robot" :size="18" weight="duotone" />
209
+ <RmIcon v-else name="User" :size="18" />
210
+ </div>
211
+ <div class="rm-ai-chat-bubble" :class="`rm-ai-chat-bubble--${message.role}`">
212
+ <div v-html="renderMarkdown(message.content)" />
213
+ <slot name="message-footer" :message="message" />
214
+ </div>
215
+ </div>
216
+
217
+ <!-- Loading indicator -->
218
+ <div v-if="loading" class="rm-ai-chat-message rm-ai-chat-message--assistant">
219
+ <div class="rm-ai-chat-avatar rm-ai-chat-avatar--assistant">
220
+ <RmIcon name="Robot" :size="18" weight="duotone" />
221
+ </div>
222
+ <div class="rm-ai-chat-bubble rm-ai-chat-bubble--assistant">
223
+ <div class="rm-ai-chat-loading">
224
+ <span class="rm-ai-chat-loading__pulse" />
225
+ <RmIcon name="CircleNotch" :size="16" class="rm-ai-chat-loading__icon" />
226
+ <span class="rm-ai-chat-loading__text">{{ loadingText }}</span>
227
+ </div>
228
+ </div>
229
+ </div>
230
+ </div>
231
+ </div>
232
+
233
+ <template #footer>
234
+ <form class="rm-ai-chat-input" @submit.prevent="handleSubmit">
235
+ <div class="rm-ai-chat-input__wrapper">
236
+ <input
237
+ v-model="inputValue"
238
+ type="text"
239
+ :placeholder="placeholder"
240
+ :disabled="loading"
241
+ @keydown.enter.prevent="handleSubmit"
242
+ />
243
+ <button type="submit" :disabled="loading || !inputValue.trim()" class="rm-ai-chat-input__send">
244
+ <RmIcon name="PaperPlaneRight" :size="18" weight="fill" />
245
+ </button>
246
+ </div>
247
+ </form>
248
+ </template>
249
+ </RmDrawer>
250
+ </template>
251
+
252
+ <style scoped>
253
+ .rm-ai-chat-trigger{align-items:center;background:linear-gradient(135deg,var(--r-color-primary-500) 0,var(--r-color-primary-600) 100%);border:none;border-radius:var(--r-radius-full);bottom:24px;box-shadow:var(--r-shadow-lg);color:var(--r-color-white);cursor:pointer;display:flex;height:52px;justify-content:center;position:fixed;right:24px;transition:all var(--r-transition-base);width:52px;z-index:89}.rm-ai-chat-trigger:hover{box-shadow:var(--r-shadow-xl);transform:scale(1.08)}.rm-ai-chat-trigger--open{opacity:.7;transform:scale(.9)}.rm-ai-chat-header{align-items:center;display:flex;gap:10px}.rm-ai-chat-header__icon{font-size:22px}.rm-ai-chat-header__robot{color:var(--r-color-primary-500)}.rm-ai-chat-header__title{color:var(--r-color-gray-900);font-size:var(--r-text-lg);font-weight:var(--r-font-weight-semibold);margin:0}.rm-ai-chat-header__clear{align-items:center;background:none;border:none;border-radius:var(--r-radius-md);color:var(--r-color-gray-400);cursor:pointer;display:flex;height:32px;justify-content:center;margin-right:8px;transition:all var(--r-transition-fast);width:32px}.rm-ai-chat-header__clear:hover{background-color:var(--r-color-gray-100);color:var(--r-color-gray-600)}.rm-ai-chat-body{display:flex;flex-direction:column;height:100%;margin:-24px}.rm-ai-chat-messages{background-color:var(--r-color-gray-50);display:flex;flex:1;flex-direction:column;gap:16px;overflow-y:auto;padding:20px}.rm-ai-chat-welcome{display:flex;flex-direction:column;gap:20px}.rm-ai-chat-bubble--welcome{background:linear-gradient(135deg,#f0f9ff,#e0f2fe)!important;border:1px solid #bae6fd}.rm-ai-chat-suggestions{align-items:center;display:flex;flex-direction:column;gap:8px}.rm-ai-chat-suggestions__hint{color:var(--r-color-gray-400);font-size:var(--r-text-xs);font-weight:var(--r-font-weight-medium);margin:0}.rm-ai-chat-suggestion{align-items:center;background:var(--r-color-white);border:1px solid var(--r-color-gray-200);border-radius:var(--r-radius-lg);color:var(--r-color-gray-600);cursor:pointer;display:flex;font-size:var(--r-text-sm);gap:8px;padding:10px 14px;text-align:left;transition:all var(--r-transition-fast);width:100%}.rm-ai-chat-suggestion:hover{background:var(--r-color-gray-50);border-color:var(--r-color-primary-400);color:var(--r-color-primary-600);transform:translateX(4px)}.rm-ai-chat-suggestion__icon{flex-shrink:0}.rm-ai-chat-message{animation:rm-ai-chat-fade-in .2s ease;display:flex;gap:10px}.rm-ai-chat-message--user{flex-direction:row-reverse}@keyframes rm-ai-chat-fade-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.rm-ai-chat-avatar{align-items:center;border-radius:var(--r-radius-full);display:flex;flex-shrink:0;height:32px;justify-content:center;width:32px}.rm-ai-chat-avatar--assistant{background:var(--r-color-primary-50);color:var(--r-color-primary-500)}.rm-ai-chat-avatar--user{background:linear-gradient(135deg,var(--r-color-primary-500) 0,var(--r-color-primary-600) 100%);color:var(--r-color-white)}.rm-ai-chat-bubble{border-radius:16px;font-size:var(--r-text-sm);line-height:var(--r-leading-relaxed);max-width:80%;padding:10px 14px}.rm-ai-chat-bubble--user{background:linear-gradient(135deg,var(--r-color-primary-500) 0,var(--r-color-primary-600) 100%);border-bottom-right-radius:4px;color:var(--r-color-white)}.rm-ai-chat-bubble--assistant{background:var(--r-color-white);border-bottom-left-radius:4px;box-shadow:0 1px 3px rgba(0,0,0,.06);color:var(--r-color-gray-700)}.rm-ai-chat-bubble :deep(ol),.rm-ai-chat-bubble :deep(ul){margin:6px 0;padding-left:20px}.rm-ai-chat-bubble :deep(li){margin:3px 0}.rm-ai-chat-bubble :deep(ol){list-style-type:decimal}.rm-ai-chat-bubble :deep(ul){list-style-type:disc}.rm-ai-chat-bubble :deep(strong){font-weight:var(--r-font-weight-semibold)}.rm-ai-chat-loading{align-items:center;display:flex;gap:8px;padding:2px 0}.rm-ai-chat-loading__pulse{animation:rm-ai-chat-pulse 2.5s ease-in-out infinite;background:rgba(99,102,241,.08);border-radius:var(--r-radius-full);height:28px;position:absolute;width:28px}.rm-ai-chat-loading__icon{animation:rm-ai-chat-spin 1.2s linear infinite;color:var(--r-color-primary-500);z-index:1}.rm-ai-chat-loading__text{color:var(--r-color-gray-500);font-size:var(--r-text-sm);font-weight:var(--r-font-weight-medium)}@keyframes rm-ai-chat-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes rm-ai-chat-pulse{0%,to{opacity:.4;transform:scale(.8)}50%{opacity:0;transform:scale(1.3)}}.rm-ai-chat-input{margin:0}.rm-ai-chat-input__wrapper{align-items:center;background:var(--r-color-gray-100);border-radius:var(--r-radius-lg);display:flex;gap:8px;padding:4px 4px 4px 16px}.rm-ai-chat-input__wrapper input{background:none;border:none;color:var(--r-color-gray-800);flex:1;font-family:inherit;font-size:var(--r-text-sm);outline:none;padding:10px 0}.rm-ai-chat-input__wrapper input::-moz-placeholder{color:var(--r-color-gray-400)}.rm-ai-chat-input__wrapper input::placeholder{color:var(--r-color-gray-400)}.rm-ai-chat-input__send{align-items:center;background:linear-gradient(135deg,var(--r-color-primary-500) 0,var(--r-color-primary-600) 100%);border:none;border-radius:var(--r-radius-md);color:var(--r-color-white);cursor:pointer;display:flex;flex-shrink:0;height:38px;justify-content:center;transition:all var(--r-transition-fast);width:38px}.rm-ai-chat-input__send:hover:not(:disabled){transform:scale(1.05)}.rm-ai-chat-input__send:disabled{cursor:not-allowed;opacity:.5}
254
+ </style>
@@ -0,0 +1,68 @@
1
+ <!--
2
+ RmAppShell - le chrome d'application du design system : barre laterale fixe
3
+ a gauche, strip d'actions fixe a droite, contenu decale entre les deux.
4
+
5
+ Structure et classes reprises de `app-shell.html`. La regle n°0 du skill est
6
+ de ne jamais reconstruire ce chrome de memoire : ce composant ne fait que
7
+ poser le markup attendu et gerer l'etat de repli.
8
+
9
+ Le decalage vit sur `body.rm-shell`, pose au montage et retire au demontage.
10
+ Sans ce retrait, une app multi-layout garderait le decalage sur ses autres
11
+ ecrans apres avoir quitte celui-ci.
12
+ -->
13
+ <script setup lang="ts">
14
+ import { onBeforeUnmount, onMounted, ref } from 'vue'
15
+ import { rmWithoutTransitions } from '../utils/transitionGuard'
16
+
17
+ const props = withDefaults(defineProps<{
18
+ /** Barre laterale repliee au chargement. */
19
+ navCollapsed?: boolean
20
+ }>(), { navCollapsed: false })
21
+
22
+ const collapsed = ref(props.navCollapsed)
23
+ const stripOpen = ref(false)
24
+
25
+ function sync() {
26
+ if (typeof document === 'undefined') return
27
+ const { body } = document
28
+ body.classList.add('rm-shell')
29
+ body.classList.toggle('lcollapsed', collapsed.value)
30
+ body.classList.toggle('ropen', stripOpen.value)
31
+ }
32
+
33
+ onMounted(sync)
34
+
35
+ onBeforeUnmount(() => {
36
+ if (typeof document === 'undefined') return
37
+ document.body.classList.remove('rm-shell', 'lcollapsed', 'ropen')
38
+ })
39
+
40
+ // Sans la garde, replier la barre laterale ne fait rien : sa largeur vient
41
+ // d'un `var()` et la propriete est sous transition (cf. utils/transitionGuard).
42
+ function toggleNav() {
43
+ rmWithoutTransitions(() => {
44
+ collapsed.value = !collapsed.value
45
+ sync()
46
+ })
47
+ }
48
+ function setStrip(open: boolean) {
49
+ rmWithoutTransitions(() => {
50
+ stripOpen.value = open
51
+ sync()
52
+ })
53
+ }
54
+
55
+ defineExpose({ collapsed, stripOpen, toggleNav, setStrip })
56
+ </script>
57
+
58
+ <template>
59
+ <div class="rm-app-shell">
60
+ <slot name="nav" :collapsed="collapsed" :toggle="toggleNav" />
61
+ <slot name="strip" :open="stripOpen" :set-open="setStrip" />
62
+ <slot />
63
+ </div>
64
+ </template>
65
+
66
+ <style scoped>
67
+ .rm-app-shell{display:contents}
68
+ </style>
@@ -0,0 +1,70 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * HybridTable — a table with inline horizontal bars behind values.
4
+ * Each row shows a label, a value, and a bar proportional to the max value.
5
+ */
6
+
7
+ export interface HybridTableRow {
8
+ label: string
9
+ value: number
10
+ }
11
+
12
+ const props = withDefaults(defineProps<{
13
+ rows: HybridTableRow[]
14
+ title?: string
15
+ color?: string
16
+ maxVisible?: number
17
+ valueFormatter?: (val: number) => string
18
+ }>(), {
19
+ title: undefined,
20
+ color: '#4164EE',
21
+ maxVisible: 10,
22
+ valueFormatter: (val: number) => val.toLocaleString(),
23
+ })
24
+
25
+ const showAll = ref(false)
26
+
27
+ const maxValue = computed(() => Math.max(...props.rows.map(r => r.value), 1))
28
+
29
+ const visibleRows = computed(() => {
30
+ if (showAll.value) return props.rows
31
+ return props.rows.slice(0, props.maxVisible)
32
+ })
33
+
34
+ const hasMore = computed(() => props.rows.length > props.maxVisible)
35
+ </script>
36
+
37
+ <template>
38
+ <div class="hybrid-table">
39
+ <h3 v-if="title" class="hybrid-table__title">{{ title }}</h3>
40
+
41
+ <div class="hybrid-table__list">
42
+ <div
43
+ v-for="(row, idx) in visibleRows"
44
+ :key="idx"
45
+ class="hybrid-table__row"
46
+ >
47
+ <span class="hybrid-table__label">{{ row.label }}</span>
48
+ <div class="hybrid-table__bar-wrapper">
49
+ <div
50
+ class="hybrid-table__bar"
51
+ :style="{ width: `${(row.value / maxValue) * 100}%`, backgroundColor: color }"
52
+ />
53
+ </div>
54
+ <span class="hybrid-table__value">{{ valueFormatter(row.value) }}</span>
55
+ </div>
56
+ </div>
57
+
58
+ <button
59
+ v-if="hasMore"
60
+ class="hybrid-table__toggle"
61
+ @click="showAll = !showAll"
62
+ >
63
+ {{ showAll ? 'Show less' : `Show all (${rows.length})` }}
64
+ </button>
65
+ </div>
66
+ </template>
67
+
68
+ <style scoped>
69
+ .hybrid-table{font-family:var(--r-font-family)}.hybrid-table__title{color:var(--r-color-gray-700);font-size:14px;font-weight:600;margin:0 0 12px}.hybrid-table__list{display:flex;flex-direction:column;gap:6px}.hybrid-table__row{align-items:center;display:flex;gap:12px;padding:6px 0}.hybrid-table__label{color:var(--r-color-gray-700);flex-shrink:0;font-size:13px;min-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hybrid-table__bar-wrapper{background:var(--r-color-gray-50);border-radius:4px;flex:1;height:20px;overflow:hidden}.hybrid-table__bar{border-radius:4px;height:100%;min-width:2px;transition:width .4s ease}.hybrid-table__value{color:var(--r-color-gray-900);flex-shrink:0;font-size:13px;font-weight:600;min-width:50px;text-align:right}.hybrid-table__toggle{background:none;border:none;color:#4164ee;cursor:pointer;font-family:var(--r-font-family);font-size:13px;margin-top:8px;padding:0;text-decoration:underline}.hybrid-table__toggle:hover{color:#3451d4}
70
+ </style>
@@ -15,12 +15,18 @@ const props = withDefaults(defineProps<{
15
15
  /** Marche d'escalier : prix, taux de remboursement, decisions datees. */
16
16
  step?: boolean
17
17
  smooth?: boolean
18
+ /**
19
+ * Libelles des axes gauche et droit. A fournir des qu'une serie porte
20
+ * `axis: 1` : deux unites sur un graphe sans libelle d'axe se lisent mal.
21
+ */
22
+ axisLabels?: [string, string]
18
23
  height?: keyof typeof RM_CHART_HEIGHTS | number
19
24
  description?: string
20
25
  }>(), {
21
26
  area: false,
22
27
  step: false,
23
28
  smooth: true,
29
+ axisLabels: undefined,
24
30
  height: 'standard',
25
31
  description: undefined,
26
32
  })
@@ -33,6 +39,7 @@ const option = computed(() =>
33
39
  step: props.step,
34
40
  smooth: props.smooth,
35
41
  decal: decal.value,
42
+ axisLabels: props.axisLabels,
36
43
  }),
37
44
  )
38
45
  </script>
@@ -0,0 +1,79 @@
1
+ <script setup lang="ts">
2
+ import { PhX } from '@phosphor-icons/vue'
3
+
4
+ export interface RmModalProps {
5
+ /** Whether the modal is open */
6
+ modelValue: boolean
7
+ /** Size: sm, md, lg, xl */
8
+ size?: 'sm' | 'md' | 'lg' | 'xl'
9
+ /** Mode: fixed (size is fixed) or free (adapts to content) */
10
+ mode?: 'fixed' | 'free'
11
+ }
12
+
13
+ const props = withDefaults(defineProps<RmModalProps>(), {
14
+ size: 'md',
15
+ mode: 'fixed',
16
+ })
17
+
18
+ const emit = defineEmits<{
19
+ 'update:modelValue': [value: boolean]
20
+ }>()
21
+
22
+ function close() {
23
+ emit('update:modelValue', false)
24
+ }
25
+
26
+ const sizeMap: Record<string, { width: string; height: string }> = {
27
+ sm: { width: '480px', height: '360px' },
28
+ md: { width: '640px', height: '500px' },
29
+ lg: { width: '800px', height: '640px' },
30
+ xl: { width: '960px', height: '780px' },
31
+ }
32
+
33
+ const modalStyle = computed(() => {
34
+ const s = sizeMap[props.size] ?? sizeMap.md
35
+ if (props.mode === 'fixed') {
36
+ return { width: s.width, height: s.height }
37
+ }
38
+ return { width: s.width, maxHeight: '85vh' }
39
+ })
40
+ </script>
41
+
42
+ <template>
43
+ <Teleport to="body">
44
+ <Transition name="rm-modal">
45
+ <div
46
+ v-if="modelValue"
47
+ class="rm-modal-overlay"
48
+ @click.self="close"
49
+ >
50
+ <div class="rm-modal" :style="modalStyle">
51
+ <div class="rm-modal__header">
52
+ <div class="rm-modal__header-content">
53
+ <slot name="header" />
54
+ </div>
55
+ <button class="rm-modal__close" @click="close">
56
+ <PhX :size="20" />
57
+ </button>
58
+ </div>
59
+
60
+ <div v-if="$slots.tabs" class="rm-modal__tabs">
61
+ <slot name="tabs" />
62
+ </div>
63
+
64
+ <div class="rm-modal__body">
65
+ <slot />
66
+ </div>
67
+
68
+ <div v-if="$slots.footer" class="rm-modal__footer">
69
+ <slot name="footer" />
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </Transition>
74
+ </Teleport>
75
+ </template>
76
+
77
+ <style scoped>
78
+ .rm-modal-overlay{align-items:center;backdrop-filter:blur(2px);background:rgba(0,0,0,.4);display:flex;inset:0;justify-content:center;position:fixed;z-index:100}.rm-modal{background:#fff;border-radius:12px;box-shadow:0 20px 60px rgba(0,0,0,.15);display:flex;flex-direction:column;max-height:90vh;max-width:95vw}.rm-modal__header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between;padding:20px 24px 0}.rm-modal__header-content{flex:1;min-width:0}.rm-modal__close{align-items:center;background:none;border:none;border-radius:var(--r-radius-md);color:var(--r-color-gray-400);cursor:pointer;display:flex;flex-shrink:0;height:32px;justify-content:center;transition:all var(--r-transition-fast);width:32px}.rm-modal__close:hover{background:var(--r-color-gray-50);color:var(--r-color-gray-700)}.rm-modal__tabs{padding:0}.rm-modal__body{flex:1;overflow-y:auto;padding:24px}.rm-modal__footer{border-top:1px solid var(--r-color-gray-200);padding:16px 24px}.rm-modal-enter-active,.rm-modal-leave-active{transition:opacity .2s ease}.rm-modal-enter-active .rm-modal,.rm-modal-leave-active .rm-modal{transition:transform .2s ease}.rm-modal-enter-from,.rm-modal-leave-to{opacity:0}.rm-modal-enter-from .rm-modal,.rm-modal-leave-to .rm-modal{transform:scale(.95)}
79
+ </style>