@keyfunc/keybricks-ds 2.1.1 → 2.4.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 (280) hide show
  1. package/DEPLOY.md +20 -5
  2. package/DESIGN.md +148 -20
  3. package/INTEGRATION.md +97 -24
  4. package/SKILL.md +11 -11
  5. package/_adherence.oxlintrc.json +56 -24
  6. package/_scripts/build-index.mjs +15 -4
  7. package/assets/icons/icon-index.js +1 -1
  8. package/assets/icons/icon-index.json +1 -1
  9. package/assets/logos/datasource/dat.png +0 -0
  10. package/assets/logos/datasource/excel.png +0 -0
  11. package/assets/logos/datasource/ftp.png +0 -0
  12. package/assets/logos/datasource/hierarchy.png +0 -0
  13. package/assets/logos/datasource/hive.png +0 -0
  14. package/assets/logos/datasource/json.png +0 -0
  15. package/assets/logos/datasource/mariadb.png +0 -0
  16. package/assets/logos/datasource/mongodb.png +0 -0
  17. package/assets/logos/datasource/mysql.png +0 -0
  18. package/assets/logos/datasource/odbc.png +0 -0
  19. package/assets/logos/datasource/oracle.png +0 -0
  20. package/assets/logos/datasource/postgresql.png +0 -0
  21. package/assets/logos/datasource/rest-api.png +0 -0
  22. package/assets/logos/datasource/sap-alt.png +0 -0
  23. package/assets/logos/datasource/sap.png +0 -0
  24. package/assets/logos/datasource/soap.png +0 -0
  25. package/assets/logos/datasource/sql-server.png +0 -0
  26. package/components/_cards/accordion.card.html +1 -1
  27. package/components/_cards/action-chip.card.html +11 -14
  28. package/components/_cards/activity.card.html +2 -2
  29. package/components/_cards/alert.card.html +2 -2
  30. package/components/_cards/app-chrome.card.html +34 -22
  31. package/components/_cards/banner.card.html +1 -1
  32. package/components/_cards/breadcrumb.card.html +4 -4
  33. package/components/_cards/button-1-base.card.html +23 -26
  34. package/components/_cards/button-2-states.card.html +14 -12
  35. package/components/_cards/button-3-sizes-icons.card.html +20 -19
  36. package/components/_cards/button-4-group.card.html +14 -31
  37. package/components/_cards/canvas-zoom-controls.card.html +1 -1
  38. package/components/_cards/cards-indicators.card.html +1 -1
  39. package/components/_cards/charts-1-sparkline.card.html +4 -4
  40. package/components/_cards/charts-10-gauge.card.html +2 -2
  41. package/components/_cards/charts-11-funnel.card.html +2 -2
  42. package/components/_cards/charts-12-treemap.card.html +2 -2
  43. package/components/_cards/charts-14-radar.card.html +3 -3
  44. package/components/_cards/charts-15-distribution.card.html +2 -2
  45. package/components/_cards/charts-16-pareto.card.html +2 -2
  46. package/components/_cards/charts-17-candlestick.card.html +2 -2
  47. package/components/_cards/charts-18-sankey.card.html +2 -2
  48. package/components/_cards/charts-19-chord.card.html +2 -2
  49. package/components/_cards/charts-2-line.card.html +3 -3
  50. package/components/_cards/charts-20-sunburst.card.html +2 -2
  51. package/components/_cards/charts-21-worldmap.card.html +3 -3
  52. package/components/_cards/charts-22-gantt.card.html +2 -2
  53. package/components/_cards/charts-23-bar3d.card.html +2 -2
  54. package/components/_cards/charts-24-pie3d.card.html +2 -2
  55. package/components/_cards/charts-25-surface3d.card.html +2 -2
  56. package/components/_cards/charts-26-scatter3d.card.html +2 -2
  57. package/components/_cards/charts-27-ribbon3d.card.html +2 -2
  58. package/components/_cards/charts-3-bar.card.html +6 -6
  59. package/components/_cards/charts-4-barlist.card.html +2 -2
  60. package/components/_cards/charts-5-donut.card.html +2 -2
  61. package/components/_cards/charts-6-scatter.card.html +2 -2
  62. package/components/_cards/charts-7-heatmap.card.html +2 -2
  63. package/components/_cards/charts-8-waterfall.card.html +2 -2
  64. package/components/_cards/charts-9-bullet.card.html +3 -3
  65. package/components/_cards/charts-99-fullpage.card.html +1 -1
  66. package/components/_cards/checkbox.card.html +6 -3
  67. package/components/_cards/codeblock.card.html +2 -2
  68. package/components/_cards/combobox.card.html +6 -4
  69. package/components/_cards/context-menu.card.html +12 -5
  70. package/components/_cards/date-picker.card.html +8 -5
  71. package/components/_cards/diffvalue.card.html +2 -2
  72. package/components/_cards/divider.card.html +2 -2
  73. package/components/_cards/drawer.card.html +3 -3
  74. package/components/_cards/dropdown-menu.card.html +2 -2
  75. package/components/_cards/dropzone.card.html +5 -3
  76. package/components/_cards/empty-state.card.html +2 -2
  77. package/components/_cards/filter-bar.card.html +1 -1
  78. package/components/_cards/icon-button.card.html +14 -14
  79. package/components/_cards/icons-0-usage.card.html +88 -0
  80. package/components/_cards/icons-1-core.card.html +4 -4
  81. package/components/_cards/icons-2-feather.card.html +4 -4
  82. package/components/_cards/icons-3-unicons.card.html +3 -3
  83. package/components/_cards/icons-4-datasource.card.html +5 -0
  84. package/components/_cards/input.card.html +5 -5
  85. package/components/_cards/kbd.card.html +1 -1
  86. package/components/_cards/keyvalue.card.html +1 -1
  87. package/components/_cards/listrow.card.html +1 -1
  88. package/components/_cards/loading.card.html +2 -2
  89. package/components/_cards/log-viewer.card.html +2 -2
  90. package/components/_cards/modal.card.html +1 -1
  91. package/components/_cards/multi-select.card.html +6 -4
  92. package/components/_cards/number-stepper.card.html +9 -6
  93. package/components/_cards/pagination.card.html +6 -4
  94. package/components/_cards/people.card.html +2 -2
  95. package/components/_cards/pipeline-node.card.html +8 -8
  96. package/components/_cards/pipeline-palette.card.html +2 -2
  97. package/components/_cards/radio.card.html +12 -7
  98. package/components/_cards/search.card.html +6 -6
  99. package/components/_cards/segmented-control.card.html +19 -12
  100. package/components/_cards/select.card.html +7 -3
  101. package/components/_cards/slider.card.html +2 -2
  102. package/components/_cards/smart-table.card.html +3 -3
  103. package/components/_cards/spreadsheet.card.html +2 -2
  104. package/components/_cards/stepper.card.html +2 -2
  105. package/components/_cards/table.card.html +79 -17
  106. package/components/_cards/tabs.card.html +10 -5
  107. package/components/_cards/tag.card.html +6 -6
  108. package/components/_cards/textarea.card.html +1 -1
  109. package/components/_cards/timeline.card.html +3 -3
  110. package/components/_cards/toast.card.html +7 -5
  111. package/components/_cards/toggles.card.html +14 -9
  112. package/components/_cards/tooltip.card.html +3 -3
  113. package/components/_cards/tree.card.html +2 -2
  114. package/components/_shared/card-kit.css +25 -0
  115. package/components/_shared/card-kit.js +50 -0
  116. package/components/_shared/src-buttons.js +1 -1
  117. package/components/_shared/src-charts.js +1 -1
  118. package/components/_shared/src-data.js +1 -1
  119. package/components/_shared/src-display.js +1 -1
  120. package/components/_shared/src-feedback.js +1 -1
  121. package/components/_shared/src-forms.js +1 -1
  122. package/components/_shared/src-icons.js +1 -1
  123. package/components/_shared/src-navigation.js +1 -1
  124. package/components/_shared/src-tabs.js +1 -1
  125. package/components/_shared/view-source.js +12 -12
  126. package/components/_vue/index.mjs +10 -0
  127. package/components/buttons/ActionChip.d.ts +1 -1
  128. package/components/buttons/Button.d.ts +1 -1
  129. package/components/buttons/Button.jsx +1 -1
  130. package/components/buttons/Button.vue +1 -1
  131. package/components/buttons/IconButton.d.ts +1 -1
  132. package/components/buttons/IconButton.jsx +5 -3
  133. package/components/buttons/IconButton.vue +3 -2
  134. package/components/charts/Bar3D.jsx +1 -1
  135. package/components/charts/Bar3D.vue +2 -2
  136. package/components/charts/BarChart.jsx +3 -3
  137. package/components/charts/BarChart.vue +6 -6
  138. package/components/charts/BarList.jsx +3 -3
  139. package/components/charts/BarList.vue +3 -3
  140. package/components/charts/BoxPlot.vue +2 -2
  141. package/components/charts/BulletChart.jsx +5 -5
  142. package/components/charts/BulletChart.vue +5 -5
  143. package/components/charts/CandlestickChart.vue +3 -3
  144. package/components/charts/ChartKit.jsx +4 -3
  145. package/components/charts/ChartTooltip.vue +1 -1
  146. package/components/charts/ChordChart.jsx +1 -1
  147. package/components/charts/ChordChart.vue +2 -2
  148. package/components/charts/DonutChart.jsx +1 -1
  149. package/components/charts/DonutChart.vue +2 -2
  150. package/components/charts/FunnelChart.jsx +3 -3
  151. package/components/charts/FunnelChart.vue +4 -4
  152. package/components/charts/GanttChart.vue +1 -1
  153. package/components/charts/GaugeChart.jsx +2 -2
  154. package/components/charts/GaugeChart.vue +2 -2
  155. package/components/charts/HeatmapChart.jsx +1 -1
  156. package/components/charts/HeatmapChart.vue +1 -1
  157. package/components/charts/Histogram.jsx +2 -2
  158. package/components/charts/Histogram.vue +4 -4
  159. package/components/charts/LineChart.vue +2 -2
  160. package/components/charts/ParetoChart.jsx +1 -1
  161. package/components/charts/ParetoChart.vue +3 -3
  162. package/components/charts/Pie3D.jsx +1 -1
  163. package/components/charts/Pie3D.vue +2 -2
  164. package/components/charts/PlotGrid.vue +1 -1
  165. package/components/charts/RadarChart.vue +1 -1
  166. package/components/charts/Ribbon3D.jsx +1 -1
  167. package/components/charts/Ribbon3D.vue +2 -2
  168. package/components/charts/SankeyChart.jsx +1 -1
  169. package/components/charts/SankeyChart.vue +2 -2
  170. package/components/charts/Scatter3D.vue +1 -1
  171. package/components/charts/ScatterChart.jsx +1 -1
  172. package/components/charts/ScatterChart.vue +3 -3
  173. package/components/charts/Sparkline.jsx +1 -1
  174. package/components/charts/Sparkline.vue +1 -1
  175. package/components/charts/SunburstChart.vue +1 -1
  176. package/components/charts/Surface3D.jsx +1 -1
  177. package/components/charts/Surface3D.vue +2 -2
  178. package/components/charts/TreemapChart.jsx +1 -1
  179. package/components/charts/TreemapChart.vue +2 -2
  180. package/components/charts/WaterfallChart.jsx +1 -1
  181. package/components/charts/WaterfallChart.vue +5 -4
  182. package/components/charts/WorldMap.jsx +4 -4
  183. package/components/charts/WorldMap.vue +5 -5
  184. package/components/data/DiffValue.d.ts +1 -0
  185. package/components/data/DiffValue.jsx +1 -1
  186. package/components/data/DiffValue.vue +1 -1
  187. package/components/data/KeyValue.d.ts +1 -1
  188. package/components/data/KeyValue.jsx +2 -2
  189. package/components/data/KeyValue.vue +2 -2
  190. package/components/data/Pagination.jsx +2 -2
  191. package/components/data/Pagination.vue +1 -1
  192. package/components/data/SmartTable.jsx +5 -5
  193. package/components/data/SmartTable.vue +6 -6
  194. package/components/data/Spreadsheet.jsx +5 -5
  195. package/components/data/Spreadsheet.vue +6 -6
  196. package/components/data/Table.d.ts +58 -4
  197. package/components/data/Table.jsx +153 -15
  198. package/components/data/Table.vue +105 -15
  199. package/components/data/data.prompt.md +1 -1
  200. package/components/display/Avatar.jsx +2 -2
  201. package/components/display/Avatar.vue +1 -1
  202. package/components/display/AvatarGroup.vue +1 -1
  203. package/components/display/Badge.d.ts +4 -1
  204. package/components/display/Badge.jsx +10 -5
  205. package/components/display/IconTile.vue +9 -4
  206. package/components/display/Kbd.d.ts +1 -1
  207. package/components/display/Kbd.jsx +1 -1
  208. package/components/display/Kbd.vue +1 -1
  209. package/components/feedback/ActivityItem.jsx +1 -1
  210. package/components/feedback/ContextMenu.d.ts +3 -2
  211. package/components/feedback/ContextMenu.jsx +8 -4
  212. package/components/feedback/ContextMenu.vue +9 -6
  213. package/components/feedback/Menu.d.ts +1 -1
  214. package/components/feedback/Modal.jsx +1 -1
  215. package/components/feedback/Modal.vue +1 -1
  216. package/components/feedback/Timeline.vue +2 -2
  217. package/components/feedback/feedback.prompt.md +1 -1
  218. package/components/forms/Combobox.d.ts +1 -1
  219. package/components/forms/DatePicker.d.ts +1 -1
  220. package/components/forms/Input.d.ts +1 -1
  221. package/components/forms/Input.jsx +2 -2
  222. package/components/forms/Input.vue +1 -1
  223. package/components/forms/MultiSelect.d.ts +1 -1
  224. package/components/forms/NumberStepper.d.ts +1 -1
  225. package/components/forms/SearchInput.d.ts +1 -1
  226. package/components/forms/Select.d.ts +1 -1
  227. package/components/forms/Select.jsx +1 -1
  228. package/components/forms/Select.vue +1 -1
  229. package/components/forms/Slider.jsx +1 -1
  230. package/components/forms/Slider.vue +1 -1
  231. package/components/forms/Switch.jsx +3 -3
  232. package/components/forms/Switch.vue +11 -8
  233. package/components/forms/Textarea.vue +1 -1
  234. package/components/forms/input-base.js +1 -1
  235. package/components/icons/Icon.jsx +17 -1
  236. package/components/icons/Icon.prompt.md +2 -0
  237. package/components/icons/Icon.vue +1 -1
  238. package/components/icons/icon-paths.js +15 -0
  239. package/components/index.d.ts +1 -0
  240. package/components/index.js +5 -4
  241. package/components/navigation/Breadcrumb.jsx +3 -7
  242. package/components/navigation/Breadcrumb.vue +2 -8
  243. package/components/navigation/SidePanel.d.ts +22 -0
  244. package/components/navigation/SidePanel.jsx +23 -0
  245. package/components/navigation/SidePanel.vue +21 -0
  246. package/components/navigation/Sidebar.d.ts +23 -7
  247. package/components/navigation/Sidebar.jsx +38 -56
  248. package/components/navigation/Sidebar.vue +40 -44
  249. package/components/navigation/TopBar.d.ts +28 -3
  250. package/components/navigation/TopBar.jsx +54 -7
  251. package/components/navigation/TopBar.vue +26 -8
  252. package/components/navigation/Tree.jsx +1 -1
  253. package/components/navigation/Tree.vue +9 -4
  254. package/components/navigation/UserMenu.vue +27 -0
  255. package/components/navigation/chrome-tones.js +22 -0
  256. package/components/navigation/nav-model.js +26 -0
  257. package/components/navigation/navigation.prompt.md +2 -0
  258. package/components/navigation/theme-store.js +9 -0
  259. package/components/tabs/CountBadge.vue +12 -0
  260. package/components/tabs/PanelTabs.vue +7 -3
  261. package/components/tabs/PillTabs.vue +8 -4
  262. package/components/tabs/SegmentedControl.d.ts +5 -3
  263. package/components/tabs/SegmentedControl.jsx +6 -4
  264. package/components/tabs/SegmentedControl.vue +4 -3
  265. package/components/tabs/Tabs.d.ts +14 -4
  266. package/components/tabs/Tabs.jsx +35 -20
  267. package/components/tabs/Tabs.vue +7 -3
  268. package/components/tabs/ViewToggle.d.ts +1 -1
  269. package/components/tabs/tabs.prompt.md +2 -2
  270. package/package.json +2 -2
  271. package/readme.md +10 -9
  272. package/readme.zh-CN.md +8 -7
  273. package/tokens/alpha.css +3 -1
  274. package/tokens/colors.css +1 -1
  275. package/tokens/dark.css +1 -1
  276. package/tokens/layout.css +8 -5
  277. package/tokens/tokens.d.ts +24 -0
  278. package/tokens/tokens.js +42 -6
  279. package/tokens/tokens.json +97 -11
  280. package/tokens/typography.css +26 -9
@@ -76,7 +76,7 @@ function mount(rootSelector, files){
76
76
  var btn = document.createElement('button');
77
77
  btn.type = 'button';
78
78
  btn.innerHTML = iconSvg() + '<span style="margin-left:6px">View Code</span>';
79
- btn.style.cssText = 'display:inline-flex;align-items:center;height:28px;padding:0 10px;border-radius:8px;border:1px solid var(--border-control,#e5e5e5);background:var(--bg-surface,#fff);color:var(--text-secondary,#737373);font-size:12px;font-family:var(--font-sans,sans-serif);cursor:pointer;';
79
+ btn.style.cssText = 'display:inline-flex;align-items:center;height:28px;box-sizing:border-box;padding:0 8px;border-radius:var(--radius-md,8px);border:1px solid var(--border-control,#e5e5e5);background:var(--bg-surface,#fff);color:var(--text-secondary,#737373);font-size:var(--text-xs,12px);line-height:var(--leading-xs,16px);font-family:var(--font-sans,sans-serif);cursor:pointer;';
80
80
  var panel = document.createElement('div');
81
81
  panel.style.cssText = 'display:none;margin-top:10px;border:1px solid var(--border-control,#e5e5e5);border-radius:8px;overflow:hidden;background:var(--bg-inset,#fafafa);';
82
82
  var headRow = document.createElement('div');
@@ -84,19 +84,19 @@ function mount(rootSelector, files){
84
84
  var tabBar = document.createElement('div');
85
85
  tabBar.style.cssText = 'display:flex;overflow-x:auto;flex:1;';
86
86
  var fwBar = document.createElement('div');
87
- fwBar.style.cssText = 'display:flex;gap:2px;padding:6px 8px;flex-shrink:0;';
87
+ fwBar.style.cssText = 'display:flex;align-items:center;flex-shrink:0;height:28px;box-sizing:border-box;gap:2px;padding:2px;margin:0 8px;border-radius:var(--radius-md,8px);background:var(--bg-inset,#fafafa);border:1px solid var(--border-control,#e5e5e5);';
88
88
  var framework = 'react';
89
89
  ['React','Vue 3'].forEach(function(fw){
90
90
  var fb = document.createElement('button');
91
91
  fb.type = 'button'; fb.textContent = fw;
92
- fb.style.cssText = 'font-size:11px;font-family:var(--font-sans,sans-serif);padding:4px 9px;border-radius:6px;border:1px solid var(--border-control,#e5e5e5);background:var(--bg-surface,#fff);color:var(--text-secondary,#737373);cursor:pointer;';
92
+ fb.style.cssText = 'display:inline-flex;align-items:center;height:22px;font-size:var(--text-xs,12px);line-height:var(--leading-xs,16px);font-weight:var(--weight-medium,500);font-family:var(--font-sans,sans-serif);padding:0 6px;border-radius:var(--radius-sm,4px);border:none;background:transparent;color:var(--text-secondary,#737373);cursor:pointer;white-space:nowrap;';
93
93
  fb.onclick = function(){
94
94
  framework = fw === 'Vue 3' ? 'vue' : 'react';
95
95
  Array.prototype.forEach.call(fwBar.children, function(el){
96
96
  var on = el.textContent === fw;
97
- el.style.background = on ? 'var(--action-primary,#155dfc)' : 'var(--bg-surface,#fff)';
98
- el.style.color = on ? 'var(--on-accent,#fff)' : 'var(--text-secondary,#737373)';
99
- el.style.borderColor = on ? 'var(--action-primary,#155dfc)' : 'var(--border-control,#e5e5e5)';
97
+ el.style.background = on ? 'var(--bg-surface,#fff)' : 'transparent'; el.style.boxShadow = on ? '0 1px 2px rgba(0,0,0,.08)' : 'none';
98
+ el.style.color = on ? 'var(--text-heading,#0a0a0a)' : 'var(--text-secondary,#737373)';
99
+
100
100
  });
101
101
  Array.prototype.forEach.call(tabBar.children, function(el, i){
102
102
  if (tabs[i].html) return;
@@ -108,9 +108,9 @@ function mount(rootSelector, files){
108
108
  };
109
109
  fwBar.appendChild(fb);
110
110
  });
111
- fwBar.firstChild.style.background = 'var(--action-primary,#155dfc)';
112
- fwBar.firstChild.style.color = 'var(--on-accent,#fff)';
113
- fwBar.firstChild.style.borderColor = 'var(--action-primary,#155dfc)';
111
+ fwBar.firstChild.style.background = 'var(--bg-surface,#fff)'; fwBar.firstChild.style.boxShadow = '0 1px 2px rgba(0,0,0,.08)';
112
+ fwBar.firstChild.style.color = 'var(--text-heading,#0a0a0a)';
113
+
114
114
  headRow.appendChild(tabBar); headRow.appendChild(fwBar);
115
115
  var body = document.createElement('div');
116
116
  var tabs = files.slice();
@@ -119,11 +119,11 @@ function mount(rootSelector, files){
119
119
  var box = document.createElement('div');
120
120
  box.style.position = 'relative';
121
121
  var pre = document.createElement('pre');
122
- pre.style.cssText = 'margin:0;padding:14px 44px 14px 16px;overflow:auto;max-height:380px;font-family:var(--font-mono,monospace);font-size:11px;line-height:16.5px;color:var(--text-body,#525252);white-space:pre;';
122
+ pre.style.cssText = 'margin:0;padding:14px 70px 14px 16px;overflow:auto;max-height:380px;font-family:var(--font-mono,monospace);font-size:var(--text-code,12px);line-height:var(--leading-code,18px);color:var(--text-body,#525252);white-space:pre;';
123
123
  var copyBtn = document.createElement('button');
124
124
  copyBtn.type = 'button';
125
125
  copyBtn.innerHTML = copySvg() + '<span style="margin-left:4px">Copy</span>';
126
- copyBtn.style.cssText = 'position:absolute;top:8px;right:10px;display:inline-flex;align-items:center;height:22px;padding:0 8px;border-radius:6px;background:var(--bg-surface,#fff);color:var(--text-secondary,#737373);font-size:11px;font-family:var(--font-sans,sans-serif);cursor:pointer;border:1px solid var(--border-control,#e5e5e5);';
126
+ copyBtn.style.cssText = 'position:absolute;top:8px;right:10px;display:inline-flex;align-items:center;height:28px;box-sizing:border-box;padding:0 8px;border-radius:var(--radius-md,8px);background:var(--bg-surface,#fff);color:var(--text-secondary,#737373);font-size:var(--text-xs,12px);line-height:var(--leading-xs,16px);font-family:var(--font-sans,sans-serif);cursor:pointer;border:1px solid var(--border-control,#e5e5e5);';
127
127
  function setText(text){
128
128
  pre.textContent = text;
129
129
  copyBtn.onclick = function(){
@@ -169,7 +169,7 @@ function mount(rootSelector, files){
169
169
  tb.type = 'button';
170
170
  tb.textContent = t.name;
171
171
  if (t.vueOnly) tb.style.display = 'none';
172
- tb.style.cssText = 'padding:8px 12px;border:none;background:transparent;font-size:12px;font-family:var(--font-sans,sans-serif);cursor:pointer;white-space:nowrap;margin-bottom:-1px;border-bottom:2px solid transparent;';
172
+ tb.style.cssText = 'padding:8px 12px;border:none;background:transparent;font-size:var(--text-xs,12px);line-height:var(--leading-xs,16px);font-family:var(--font-sans,sans-serif);cursor:pointer;white-space:nowrap;margin-bottom:-1px;border-bottom:2px solid transparent;';
173
173
  tb.onclick = function(){ selectTab(i); };
174
174
  tabBar.appendChild(tb);
175
175
  });
@@ -114,10 +114,12 @@ export { default as Breadcrumb } from "../navigation/Breadcrumb.vue";
114
114
  export { default as DocTree } from "../navigation/DocTree.vue";
115
115
  export { default as PageHeading } from "../navigation/PageHeading.vue";
116
116
  export { default as PageToolbar } from "../navigation/PageToolbar.vue";
117
+ export { default as SidePanel } from "../navigation/SidePanel.vue";
117
118
  export { default as Sidebar } from "../navigation/Sidebar.vue";
118
119
  export { default as ThemeToggle } from "../navigation/ThemeToggle.vue";
119
120
  export { default as TopBar } from "../navigation/TopBar.vue";
120
121
  export { default as Tree } from "../navigation/Tree.vue";
122
+ export { default as UserMenu } from "../navigation/UserMenu.vue";
121
123
 
122
124
  /* pipeline/ */
123
125
  export { default as ConfigPanel } from "../pipeline/ConfigPanel.vue";
@@ -132,8 +134,16 @@ export { default as PortLabel } from "../pipeline/PortLabel.vue";
132
134
  export { default as ZoomControls } from "../pipeline/ZoomControls.vue";
133
135
 
134
136
  /* tabs/ */
137
+ export { default as CountBadge } from "../tabs/CountBadge.vue";
135
138
  export { default as PanelTabs } from "../tabs/PanelTabs.vue";
136
139
  export { default as PillTabs } from "../tabs/PillTabs.vue";
137
140
  export { default as SegmentedControl } from "../tabs/SegmentedControl.vue";
138
141
  export { default as Tabs } from "../tabs/Tabs.vue";
139
142
  export { default as ViewToggle } from "../tabs/ViewToggle.vue";
143
+
144
+ /* shared values */
145
+ export { KEYBRICKS_MGMT, KEYBRICKS_NAV, KEYBRICKS_NAV_FOOTER } from "../navigation/nav-model.js";
146
+ export { applyStoredTheme } from "../navigation/theme-store.js";
147
+ export { PIPELINE_CATALOG } from "../pipeline/pipeline-catalog.js";
148
+ export { VUE_ICON_PATHS } from "../icons/icon-paths.js";
149
+ export { FEATHER_PATHS } from "../icons/feather-paths.js";
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
  /** Tinted bulk-action chip (comparison-table actions). Tier height and text (32px/14px at md), r8, weight 500. */
3
3
  export interface ActionChipProps {
4
- /** Size tier: sm 24 · md 32 · lg 36. Omit to inherit the enclosing region's tier (the default). */
4
+ /** Size tier: sm 28 · md 32 · lg 40. Omit to inherit the enclosing region's tier (the default). */
5
5
  size?: "sm" | "md" | "lg";
6
6
  /** success #ecfdf5/#007a55 · warning #fff7ed/#ca3500 · neutral #f5f5f5/#525252 · info · danger */
7
7
  tone?: "success" | "warning" | "neutral" | "info" | "danger";
@@ -5,7 +5,7 @@ import * as React from "react";
5
5
  export interface ButtonProps {
6
6
  /** primary (blue #155dfc) | dark (#171717, e.g. "AI Assistant") | outline | ghost | primary-soft (blue tint toggle, e.g. "Data Preview") | danger (solid red, destructive) | danger-soft (red tint, e.g. "Remove") | text (no background/border ever — underlines on hover, e.g. "View all", "Cancel") */
7
7
  variant?: "primary" | "dark" | "outline" | "ghost" | "primary-soft" | "danger" | "danger-soft" | "text";
8
- /** Size tier: sm 24px/12px text · md 32px/14px · lg 36px/14px. Omit to inherit the tier of the enclosing region
8
+ /** Size tier: sm 28px/12px text (13px in CJK) · md 32px/14px · lg 40px/14px. Omit to inherit the tier of the enclosing region
9
9
  * (toolbar, form, table, dialog — or the app root), which is the default and the normal case: controls that share
10
10
  * a row must share a tier. Set it only on a control that stands alone. */
11
11
  size?: "sm" | "md" | "lg";
@@ -35,7 +35,7 @@ function Spinner({ size, color }) {
35
35
  return <span style={{ display: "inline-block", width: size, height: size, ...(size ? null : tierIcon), borderRadius: "var(--radius-full)", border: "2px solid currentColor", borderTopColor: "transparent", opacity: .9, color, animation: "kb-spin .7s linear infinite", flexShrink: 0 }} />;
36
36
  }
37
37
 
38
- /** Keybricks button. variant: primary | dark | outline | ghost | primary-soft | danger | danger-soft | text; size: sm 24 | md 32 | lg 36 — omit it to inherit the region's tier (the default). Interaction states: hover (one step darker) · press (two steps, set via mouse-down) · keyboard focus ring · loading · disabled. */
38
+ /** Keybricks button. variant: primary | dark | outline | ghost | primary-soft | danger | danger-soft | text; size: sm 28 | md 32 | lg 40 — omit it to inherit the region's tier (the default). Interaction states: hover (one step darker) · press (two steps, set via mouse-down) · keyboard focus ring · loading · disabled. */
39
39
  export function Button({ variant = "primary", size, icon, iconSize, iconPosition = "left", medium, disabled, loading, fullWidth, iconOnly, label, pressed, focused, children, style, onClick, ...rest }) {
40
40
  const [hover, setHover] = React.useState(false);
41
41
  const [press, setPress] = React.useState(false);
@@ -22,7 +22,7 @@ const hovers = { primary: 'var(--action-primary-hover)', dark: 'var(--action-dar
22
22
  const presses = { primary: 'var(--action-primary-active)', dark: 'var(--action-dark-active)', outline: 'var(--bg-press)', ghost: 'var(--bg-press)', 'primary-soft': 'var(--soft-active)', danger: 'var(--danger-active)', 'danger-soft': 'var(--danger-soft-active)' }
23
23
  const colorHoverVariants = { text: 'var(--text-heading)' }
24
24
 
25
- // Button — variant: primary | dark | outline | ghost | primary-soft | danger | danger-soft | text; size: sm 24 | md 32 | lg 36 — omit it to inherit the region's tier (the default).
25
+ // Button — variant: primary | dark | outline | ghost | primary-soft | danger | danger-soft | text; size: sm 28 | md 32 | lg 40 — omit it to inherit the region's tier (the default).
26
26
  const props = defineProps({
27
27
  variant: { type: String, default: 'primary' }, size: String,
28
28
  icon: String, iconSize: Number, iconPosition: { type: String, default: 'left' },
@@ -3,7 +3,7 @@ import * as React from "react";
3
3
  export interface IconButtonProps {
4
4
  /** Icon glyph name */
5
5
  icon: string;
6
- /** Size tier: a square of sm 24 · md 32 · lg 36 with a 14/16/16 icon. Omit to inherit the enclosing region's tier
6
+ /** Size tier: a square of sm 28 · md 32 · lg 40 with a 14/16/16 icon. Omit to inherit the enclosing region's tier
7
7
  * (the default). "xs" is a deprecated alias of "sm". */
8
8
  size?: "xs" | "sm" | "md" | "lg";
9
9
  /** Pressed/selected state (#f5f5f5 wash) */
@@ -5,7 +5,7 @@ import { Icon } from "../icons/Icon.jsx";
5
5
  data-size on the button; omit it to inherit — the default. "xs" is kept as an alias of "sm" for 1.x callers. */
6
6
  const tierIcon = { width: "var(--control-icon)", height: "var(--control-icon)" };
7
7
 
8
- /** Square ghost icon button (toolbar/back/bell/view toggles). size: sm 24 | md 32 | lg 36 — omit to inherit the region's tier. danger tints red; loading shows a spinner and disables interaction. States: hover · press · keyboard focus ring · active (persistent toggle-on) · disabled. */
8
+ /** Square ghost icon button (toolbar/back/bell/view toggles). size: sm 28 | md 32 | lg 40 — omit to inherit the region's tier. danger tints red; loading shows a spinner and disables interaction. States: hover · press · keyboard focus ring · active (persistent toggle-on) · disabled. */
9
9
  export function IconButton({ icon, size, active, muted, danger, disabled, loading, label, pressed, focused, style, onClick, ...rest }) {
10
10
  const [hover, setHover] = React.useState(false);
11
11
  const [press, setPress] = React.useState(false);
@@ -14,9 +14,11 @@ export function IconButton({ icon, size, active, muted, danger, disabled, loadin
14
14
  const busy = disabled || loading;
15
15
  const isPressed = (press || pressed) && !busy;
16
16
  const color = danger ? "var(--danger)" : muted ? "var(--text-muted)" : "var(--text-body)";
17
- const bg = isPressed ? (danger ? "var(--danger-soft-active)" : "var(--bg-press)")
17
+ // hover / press are alpha overlays, not solid steps: the button sits on white rows, grey table headers and grey bars alike,
18
+ // and an overlay darkens whatever is underneath by the same amount (a solid --bg-hover vanishes on a --bg-table-header).
19
+ const bg = isPressed ? (danger ? "var(--danger-soft-active)" : "var(--overlay-press)")
18
20
  : active ? "var(--bg-active)"
19
- : hover && !busy ? (danger ? "var(--danger-bg)" : "var(--bg-hover)") : "transparent";
21
+ : hover && !busy ? (danger ? "var(--danger-bg)" : "var(--overlay-hover)") : "transparent";
20
22
  return (
21
23
  <button type="button" data-press="own" data-size={tier} aria-label={label} disabled={busy} onClick={onClick}
22
24
  onMouseEnter={() => setHover(true)} onMouseLeave={() => { setHover(false); setPress(false); }}
@@ -4,7 +4,7 @@ import Icon from '../icons/Icon.vue'
4
4
  // A square of the region's control height, icon at the tier's icon size (tokens/layout.css). `size` stamps data-size on
5
5
  // the button; omit it to inherit — the default. "xs" is kept as an alias of "sm" for 1.x callers.
6
6
  const tierIcon = { width: 'var(--control-icon)', height: 'var(--control-icon)' }
7
- // Square ghost icon button (toolbar/back/bell/view toggles). size: sm 24 | md 32 | lg 36 — omit to inherit the region's tier.
7
+ // Square ghost icon button (toolbar/back/bell/view toggles). size: sm 28 | md 32 | lg 40 — omit to inherit the region's tier.
8
8
  const props = defineProps({ icon: String, size: String, active: Boolean, muted: Boolean, danger: Boolean, disabled: Boolean, loading: Boolean, label: String, pressed: Boolean, focused: Boolean, style: Object })
9
9
  defineEmits(['click'])
10
10
  const hover = ref(false), press = ref(false), focus = ref(false)
@@ -12,7 +12,8 @@ const tier = computed(() => props.size === 'xs' ? 'sm' : props.size)
12
12
  const busy = computed(() => props.disabled || props.loading)
13
13
  const isPressed = computed(() => (press.value || props.pressed) && !busy.value)
14
14
  const color = computed(() => props.danger ? 'var(--danger)' : props.muted ? 'var(--text-muted)' : 'var(--text-body)')
15
- const bg = computed(() => isPressed.value ? (props.danger ? 'var(--danger-soft-active)' : 'var(--bg-press)') : props.active ? 'var(--bg-active)' : (hover.value && !busy.value ? (props.danger ? 'var(--danger-bg)' : 'var(--bg-hover)') : 'transparent'))
15
+ // hover / press are alpha overlays so the step is the same on white rows, grey table headers and grey bars
16
+ const bg = computed(() => isPressed.value ? (props.danger ? 'var(--danger-soft-active)' : 'var(--overlay-press)') : props.active ? 'var(--bg-active)' : (hover.value && !busy.value ? (props.danger ? 'var(--danger-bg)' : 'var(--overlay-hover)') : 'transparent'))
16
17
  const s = computed(() => ({ width: 'var(--control-h)', height: 'var(--control-h)', borderRadius: 'var(--radius-md)', border: 'none', cursor: busy.value ? 'default' : 'pointer', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', background: bg.value, color: color.value, opacity: busy.value ? .5 : 1, boxShadow: (focus.value || props.focused) && !busy.value ? 'var(--ring-focus)' : 'none', transition: 'var(--t-hover)', padding: 0, flexShrink: 0, ...props.style }))
17
18
  </script>
18
19
  <template>
@@ -53,7 +53,7 @@ export function Bar3D({ series = [], categories = [], width = 460, height = 300,
53
53
  {vTicks.map((t, i) => (
54
54
  <g key={"vt" + i}>
55
55
  <line x1={t.p.x - 4} y1={t.p.y} x2={t.p.x} y2={t.p.y} stroke="var(--chart-guide)" strokeWidth="1" />
56
- <text x={t.p.x - 7} y={t.p.y + 3} textAnchor="end" style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", fill: "var(--chart-label)" }}>{t.v}</text>
56
+ <text x={t.p.x - 7} y={t.p.y + 3} textAnchor="end" style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", fill: "var(--chart-label)" }}>{t.v}</text>
57
57
  </g>
58
58
  ))}
59
59
  {categories.map((c, i) => {
@@ -56,7 +56,7 @@ const g = computed(() => {
56
56
  <line :x1="g.vAxis[0].x" :y1="g.vAxis[0].y" :x2="g.vAxis[1].x" :y2="g.vAxis[1].y" stroke="var(--chart-guide)" stroke-width="1"/>
57
57
  <g v-for="(t,i) in g.vTicks" :key="'vt'+i">
58
58
  <line :x1="t.p.x-4" :y1="t.p.y" :x2="t.p.x" :y2="t.p.y" stroke="var(--chart-guide)" stroke-width="1"/>
59
- <text :x="t.p.x-7" :y="t.p.y+3" text-anchor="end" style="font-family:var(--font-mono);font-size:var(--text-2xs);fill:var(--chart-label)">{{ t.v }}</text>
59
+ <text :x="t.p.x-7" :y="t.p.y+3" text-anchor="end" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ t.v }}</text>
60
60
  </g>
61
61
  <text v-for="(l,i) in g.labels" :key="'c'+i" :x="l.x" :y="l.y" text-anchor="middle" style="font-family:var(--font-sans);font-size:var(--text-2xs);fill:var(--chart-label)">{{ l.c }}</text>
62
62
  <g v-for="b in g.drawn" :key="b.key" :opacity="hover==null||hover===b.key?1:0.45"
@@ -67,7 +67,7 @@ const g = computed(() => {
67
67
  </svg>
68
68
  <div v-if="hover!=null && interactive" :style="{position:'absolute',left:g.drawn.find(b=>b.key===hover).top.x+'px',top:g.drawn.find(b=>b.key===hover).top.y+'px',transform:g.drawn.find(b=>b.key===hover).top.x>width*0.6?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',lineHeight:'normal',whiteSpace:'nowrap'}">
69
69
  <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ categories[g.drawn.find(b=>b.key===hover).i] }}</div>
70
- <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ g.drawn.find(b=>b.key===hover).s.name }} · {{ valueFormat(g.drawn.find(b=>b.key===hover).v) }}</div>
70
+ <div style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading)">{{ g.drawn.find(b=>b.key===hover).s.name }} · {{ valueFormat(g.drawn.find(b=>b.key===hover).v) }}</div>
71
71
  </div>
72
72
  <div style="display:flex;gap:14px;padding-top:6px;line-height:normal">
73
73
  <span v-for="(s,i) in series" :key="s.name" style="display:inline-flex;align-items:center;gap:6px">
@@ -92,7 +92,7 @@ export function BarChart({ series = [], categories = [], mode = "grouped", orien
92
92
  {ticks.map((t) => (
93
93
  <g key={t}>
94
94
  <line x1={sv(t)} x2={sv(t)} y1={pad.t} y2={height - pad.b} stroke={t === 0 ? "var(--chart-axis)" : "var(--chart-grid)"} strokeWidth="1" />
95
- <text x={sv(t)} y={height - pad.b + 15} textAnchor="middle" style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", fill: "var(--chart-label)" }}>{pct ? t + "%" : yFormat(t)}</text>
95
+ <text x={sv(t)} y={height - pad.b + 15} textAnchor="middle" style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", fill: "var(--chart-label)" }}>{pct ? t + "%" : yFormat(t)}</text>
96
96
  </g>
97
97
  ))}
98
98
  {categories.map((c, i) => (
@@ -107,13 +107,13 @@ export function BarChart({ series = [], categories = [], mode = "grouped", orien
107
107
  {showValues ? rects.map((r) => (
108
108
  (horiz ? r.w : r.h) < 22 ? null : (
109
109
  <text key={"v" + r.key} x={horiz ? r.x + r.w - 5 : r.x + r.w / 2} y={horiz ? r.y + r.h / 2 + 3.5 : r.y + 12}
110
- textAnchor={horiz ? "end" : "middle"} style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", fill: "var(--on-accent)" }}>{fmtVal(r.s, r.i)}</text>
110
+ textAnchor={horiz ? "end" : "middle"} style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", fill: "var(--on-accent)" }}>{fmtVal(r.s, r.i)}</text>
111
111
  )
112
112
  )) : null}
113
113
  {oScale ? <g>
114
114
  <path d={linePath(overlay.values.map((v, i) => [mid(i), oScale.sy(v)]))} fill="none" stroke={overlay.color || "var(--chart-total)"} strokeWidth="1.5" />
115
115
  {overlay.values.map((v, i) => <circle key={i} cx={mid(i)} cy={oScale.sy(v)} r="2.5" fill={overlay.color || "var(--chart-total)"} />)}
116
- {axes ? oScale.ticks.map((t) => <text key={t} x={width - pad.r + 6} y={oScale.sy(t) + 3} style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", fill: "var(--chart-label)" }}>{overlayFormat(t)}</text>) : null}
116
+ {axes ? oScale.ticks.map((t) => <text key={t} x={width - pad.r + 6} y={oScale.sy(t) + 3} style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", fill: "var(--chart-label)" }}>{overlayFormat(t)}</text>) : null}
117
117
  </g> : null}
118
118
  </svg>
119
119
  {hover != null && interactive ? (
@@ -89,8 +89,8 @@ function rowVal(s){
89
89
  <g v-for="t in g.ticks" :key="t">
90
90
  <line v-if="g.horiz" :x1="g.sv(t)" :x2="g.sv(t)" :y1="g.pad.t" :y2="height-g.pad.b" :stroke="t===0?'var(--chart-axis)':'var(--chart-grid)'" stroke-width="1"/>
91
91
  <line v-else :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sv(t)" :y2="g.sv(t)" :stroke="t===0?'var(--chart-axis)':'var(--chart-grid)'" stroke-width="1"/>
92
- <text v-if="g.horiz" :x="g.sv(t)" :y="height-g.pad.b+15" text-anchor="middle" style="font-family:var(--font-mono);font-size:var(--text-2xs);fill:var(--chart-label)">{{ g.fmtAxis(t) }}</text>
93
- <text v-else :x="g.pad.l-8" :y="g.sv(t)+3" text-anchor="end" style="font-family:var(--font-mono);font-size:var(--text-2xs);fill:var(--chart-label)">{{ g.fmtAxis(t) }}</text>
92
+ <text v-if="g.horiz" :x="g.sv(t)" :y="height-g.pad.b+15" text-anchor="middle" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ g.fmtAxis(t) }}</text>
93
+ <text v-else :x="g.pad.l-8" :y="g.sv(t)+3" text-anchor="end" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ g.fmtAxis(t) }}</text>
94
94
  </g>
95
95
  <text v-for="(c,i) in categories" :key="'c'+i"
96
96
  :x="g.horiz ? g.pad.l-8 : g.mid(i)" :y="g.horiz ? g.mid(i)+3.5 : height-g.pad.b+16"
@@ -103,12 +103,12 @@ function rowVal(s){
103
103
  <template v-if="showValues">
104
104
  <text v-for="r in g.rects.filter(r=>(g.horiz?r.w:r.h)>=22)" :key="'v'+r.key"
105
105
  :x="g.horiz ? r.x+r.w-5 : r.x+r.w/2" :y="g.horiz ? r.y+r.h/2+3.5 : r.y+12"
106
- :text-anchor="g.horiz ? 'end' : 'middle'" style="font-family:var(--font-mono);font-size:var(--text-2xs);fill:var(--on-accent)">{{ r.text }}</text>
106
+ :text-anchor="g.horiz ? 'end' : 'middle'" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--on-accent)">{{ r.text }}</text>
107
107
  </template>
108
108
  <template v-if="g.ov">
109
109
  <path :d="g.ov.path" fill="none" :stroke="g.ov.color" stroke-width="1.5"/>
110
110
  <circle v-for="(d,i) in g.ov.dots" :key="'o'+i" :cx="d[0]" :cy="d[1]" r="2.5" :fill="g.ov.color"/>
111
- <text v-if="axes" v-for="t in g.ov.ticks" :key="'ot'+t" :x="width-g.pad.r+6" :y="g.ov.osy(t)+3" style="font-family:var(--font-mono);font-size:var(--text-2xs);fill:var(--chart-label)">{{ overlayFormat(t) }}</text>
111
+ <text v-if="axes" v-for="t in g.ov.ticks" :key="'ot'+t" :x="width-g.pad.r+6" :y="g.ov.osy(t)+3" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ overlayFormat(t) }}</text>
112
112
  </template>
113
113
  </svg>
114
114
  <div v-if="hover!=null && interactive" :style="{position:'absolute',left:(g.horiz ? Math.min(width-20, g.pad.l+(width-g.pad.l-g.pad.r)*0.5) : g.mid(hover))+'px',top:(g.horiz ? g.pad.t+g.band*hover+g.band/2 : height/2-10)+'px',transform:(g.horiz || g.mid(hover)>width*0.62)?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',lineHeight:'normal',whiteSpace:'nowrap'}">
@@ -116,12 +116,12 @@ function rowVal(s){
116
116
  <div v-for="s in g.shown" :key="s.name" style="display:flex;align-items:center;gap:6px;padding:1px 0">
117
117
  <span :style="{width:'7px',height:'7px',borderRadius:'2px',background:mode==='diverging'?(s.values[hover]>=0?'var(--chart-pos)':'var(--chart-neg)'):g.colorOf(s),flexShrink:0}"/>
118
118
  <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary)">{{ s.name }}</span>
119
- <span style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading);margin-left:auto;padding-left:10px">{{ rowVal(s) }}</span>
119
+ <span style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading);margin-left:auto;padding-left:10px">{{ rowVal(s) }}</span>
120
120
  </div>
121
121
  <div v-if="overlay" style="display:flex;align-items:center;gap:6px;padding:1px 0">
122
122
  <span :style="{width:'7px',height:'7px',borderRadius:'2px',background:overlay.color||'var(--chart-total)'}"/>
123
123
  <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary)">{{ overlay.name }}</span>
124
- <span style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading);margin-left:auto;padding-left:10px">{{ overlayFormat(overlay.values[hover]) }}</span>
124
+ <span style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading);margin-left:auto;padding-left:10px">{{ overlayFormat(overlay.values[hover]) }}</span>
125
125
  </div>
126
126
  </div>
127
127
  <div v-if="legend && (series.length>1 || overlay)" style="display:flex;flex-wrap:wrap;gap:4px 14px;padding-top:8px;line-height:normal">
@@ -17,9 +17,9 @@ export function BarList({ rows = [], max, width = 360, valueFormat = fmtNum, sho
17
17
  style={{ opacity: hover == null || hover === r.label ? 1 : 0.5, cursor: onSelect ? "pointer" : "default", transition: "var(--t-hover)" }}>
18
18
  <div style={{ display: "flex", alignItems: "baseline", gap: 8, paddingBottom: 4 }}>
19
19
  <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", color: "var(--text-body)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{r.label}</span>
20
- <span style={{ marginLeft: "auto", fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)", color: "var(--text-heading)" }}>{valueFormat(r.value)}</span>
21
- {hover === r.label && r.delta == null ? <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", color: "var(--text-muted)", paddingLeft: 6 }}>{((r.value / total) * 100).toFixed(1)}%</span> : null}
22
- {r.delta != null ? <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", color: r.delta >= 0 ? "var(--chart-pos)" : "var(--chart-neg)", minWidth: 38, textAlign: "right" }}>{(r.delta >= 0 ? "+" : "") + r.delta.toFixed(1)}%</span> : null}
20
+ <span style={{ marginLeft: "auto", fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-xs)", color: "var(--text-heading)" }}>{valueFormat(r.value)}</span>
21
+ {hover === r.label && r.delta == null ? <span style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", color: "var(--text-muted)", paddingLeft: 6 }}>{((r.value / total) * 100).toFixed(1)}%</span> : null}
22
+ {r.delta != null ? <span style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", color: r.delta >= 0 ? "var(--chart-pos)" : "var(--chart-neg)", minWidth: 38, textAlign: "right" }}>{(r.delta >= 0 ? "+" : "") + r.delta.toFixed(1)}%</span> : null}
23
23
  </div>
24
24
  <div style={{ height: barHeight, borderRadius: "var(--radius-full)", background: showTrack ? "var(--chart-track)" : "transparent", overflow: "hidden" }}>
25
25
  <div style={{ width: Math.max(1, (r.value / top) * 100) + "%", height: "100%", borderRadius: "var(--radius-full)", background: r.color || "var(--series-1)", filter: hover === r.label ? "brightness(1.08)" : "none", transition: "width var(--dur) var(--ease-out), filter var(--dur-fast) var(--ease-out)" }} />
@@ -13,9 +13,9 @@ const hover = ref(null)
13
13
  :style="{opacity:hover==null||hover===r.label?1:0.5,transition:'var(--t-hover)'}">
14
14
  <div style="display:flex;align-items:baseline;gap:8px;padding-bottom:4px">
15
15
  <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-body);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">{{ r.label }}</span>
16
- <span style="margin-left:auto;font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ valueFormat(r.value) }}</span>
17
- <span v-if="hover===r.label && r.delta==null" style="font-family:var(--font-mono);font-size:var(--text-2xs);color:var(--text-muted);padding-left:6px">{{ ((r.value/total)*100).toFixed(1) }}%</span>
18
- <span v-if="r.delta!=null" :style="{fontFamily:'var(--font-mono)',fontSize:'var(--text-2xs)',color:r.delta>=0?'var(--chart-pos)':'var(--chart-neg)',minWidth:'38px',textAlign:'right'}">{{ (r.delta>=0?'+':'')+r.delta.toFixed(1) }}%</span>
16
+ <span style="margin-left:auto;font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading)">{{ valueFormat(r.value) }}</span>
17
+ <span v-if="hover===r.label && r.delta==null" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);color:var(--text-muted);padding-left:6px">{{ ((r.value/total)*100).toFixed(1) }}%</span>
18
+ <span v-if="r.delta!=null" :style="{fontFamily: 'var(--font-sans)', fontVariantNumeric: 'tabular-nums',fontSize:'var(--text-2xs-mono)',color:r.delta>=0?'var(--chart-pos)':'var(--chart-neg)',minWidth:'38px',textAlign:'right'}">{{ (r.delta>=0?'+':'')+r.delta.toFixed(1) }}%</span>
19
19
  </div>
20
20
  <div :style="{height:barHeight+'px',borderRadius:'var(--radius-full)',background:showTrack?'var(--chart-track)':'transparent',overflow:'hidden'}">
21
21
  <div :style="{width:Math.max(1,(r.value/top)*100)+'%',height:'100%',borderRadius:'var(--radius-full)',background:r.color||'var(--series-1)',filter:hover===r.label?'brightness(1.08)':'none',transition:'width var(--dur) var(--ease-out), filter var(--dur-fast) var(--ease-out)'}"/>
@@ -34,7 +34,7 @@ function move(e){
34
34
  <template v-if="axes">
35
35
  <g v-for="t in g.ticks" :key="t">
36
36
  <line :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sy(t)" :y2="g.sy(t)" :stroke="t===0?'var(--chart-axis)':'var(--chart-grid)'" stroke-width="1"/>
37
- <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-mono);font-size:var(--text-2xs);fill:var(--chart-label)">{{ valueFormat(t) }}</text>
37
+ <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ valueFormat(t) }}</text>
38
38
  </g>
39
39
  </template>
40
40
  <g v-for="(b,i) in g.boxes" :key="b.label" :opacity="hover==null||hover===i?1:0.45" style="transition:opacity var(--dur-fast) var(--ease-out)">
@@ -50,7 +50,7 @@ function move(e){
50
50
  </svg>
51
51
  <div v-if="hover!=null && interactive" :style="{position:'absolute',left:g.boxes[hover].x+'px',top:(height/2-10)+'px',transform:g.boxes[hover].x>width*0.62?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',lineHeight:'normal',whiteSpace:'nowrap'}">
52
52
  <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ g.boxes[hover].label }}</div>
53
- <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">
53
+ <div style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading)">
54
54
  {{ valueFormat(g.boxes[hover].lo) }} · Q1 {{ valueFormat(g.boxes[hover].q1) }} · med {{ valueFormat(g.boxes[hover].med) }} · Q3 {{ valueFormat(g.boxes[hover].q3) }} · {{ valueFormat(g.boxes[hover].hi) }}
55
55
  </div>
56
56
  </div>
@@ -21,14 +21,14 @@ export function BulletChart({ rows = [], width = 360, valueFormat = fmtNum, tone
21
21
  style={{ opacity: hover == null || on ? 1 : 0.55, transition: "var(--t-hover)" }}>
22
22
  <div style={{ display: "flex", alignItems: "baseline", paddingBottom: 4 }}>
23
23
  <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", color: "var(--text-body)" }}>{r.label}</span>
24
- <span style={{ marginLeft: "auto", fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)", color: "var(--text-heading)" }}>{valueFormat(r.value)}</span>
25
- <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", color: "var(--text-muted)", paddingLeft: 6 }}>/ {valueFormat(r.target)}</span>
26
- {on ? <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", color: missed ? "var(--chart-neg)" : "var(--chart-pos)", paddingLeft: 8 }}>{(missed ? "" : "+") + (((r.value - r.target) / (r.target || 1)) * 100).toFixed(1)}%</span> : null}
24
+ <span style={{ marginLeft: "auto", fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-xs)", color: "var(--text-heading)" }}>{valueFormat(r.value)}</span>
25
+ <span style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", color: "var(--text-muted)", paddingLeft: 6 }}>/ {valueFormat(r.target)}</span>
26
+ {on ? <span style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", color: missed ? "var(--chart-neg)" : "var(--chart-pos)", paddingLeft: 8 }}>{(missed ? "" : "+") + (((r.value - r.target) / (r.target || 1)) * 100).toFixed(1)}%</span> : null}
27
27
  </div>
28
28
  <div style={{ position: "relative", height: 12, borderRadius: 3, background: "var(--chart-track)", overflow: "hidden" }}>
29
29
  {r.ranges ? <>
30
- <div style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: pc(r.ranges[0]), background: "var(--alpha-heavy, rgba(0,0,0,.12))", opacity: 0.5 }} />
31
- <div style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: pc(r.ranges[1]), background: "var(--alpha-heavy, rgba(0,0,0,.12))", opacity: 0.25 }} />
30
+ <div style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: pc(r.ranges[0]), background: "var(--neutral-alpha-12)", opacity: 0.5 }} />
31
+ <div style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: pc(r.ranges[1]), background: "var(--neutral-alpha-12)", opacity: 0.25 }} />
32
32
  </> : null}
33
33
  <div style={{ position: "absolute", left: 0, top: 3, bottom: 3, width: pc(r.value), borderRadius: 2, background: bar, transition: "width var(--dur) var(--ease-out)" }} />
34
34
  <div style={{ position: "absolute", left: pc(r.target), top: 0, bottom: 0, width: 2, background: "var(--chart-total)" }} />
@@ -16,14 +16,14 @@ function geom(r){
16
16
  :style="{opacity:hover==null||hover===r.label?1:0.55,transition:'var(--t-hover)'}">
17
17
  <div style="display:flex;align-items:baseline;padding-bottom:4px">
18
18
  <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-body)">{{ r.label }}</span>
19
- <span style="margin-left:auto;font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ valueFormat(r.value) }}</span>
20
- <span style="font-family:var(--font-mono);font-size:var(--text-2xs);color:var(--text-muted);padding-left:6px">/ {{ valueFormat(r.target) }}</span>
21
- <span v-if="hover===r.label" :style="{fontFamily:'var(--font-mono)',fontSize:'var(--text-2xs)',paddingLeft:'8px',color:r.value<r.target?'var(--chart-neg)':'var(--chart-pos)'}">{{ (r.value<r.target?'':'+')+(((r.value-r.target)/(r.target||1))*100).toFixed(1) }}%</span>
19
+ <span style="margin-left:auto;font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading)">{{ valueFormat(r.value) }}</span>
20
+ <span style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);color:var(--text-muted);padding-left:6px">/ {{ valueFormat(r.target) }}</span>
21
+ <span v-if="hover===r.label" :style="{fontFamily: 'var(--font-sans)', fontVariantNumeric: 'tabular-nums',fontSize:'var(--text-2xs-mono)',paddingLeft:'8px',color:r.value<r.target?'var(--chart-neg)':'var(--chart-pos)'}">{{ (r.value<r.target?'':'+')+(((r.value-r.target)/(r.target||1))*100).toFixed(1) }}%</span>
22
22
  </div>
23
23
  <div style="position:relative;height:12px;border-radius:3px;background:var(--chart-track);overflow:hidden">
24
24
  <template v-if="r.ranges">
25
- <div :style="{position:'absolute',left:0,top:0,bottom:0,width:geom(r).pc(r.ranges[0]),background:'rgba(0,0,0,.12)',opacity:.5}"/>
26
- <div :style="{position:'absolute',left:0,top:0,bottom:0,width:geom(r).pc(r.ranges[1]),background:'rgba(0,0,0,.12)',opacity:.25}"/>
25
+ <div :style="{position:'absolute',left:0,top:0,bottom:0,width:geom(r).pc(r.ranges[0]),background:'var(--neutral-alpha-12)',opacity:.5}"/>
26
+ <div :style="{position:'absolute',left:0,top:0,bottom:0,width:geom(r).pc(r.ranges[1]),background:'var(--neutral-alpha-12)',opacity:.25}"/>
27
27
  </template>
28
28
  <div :style="{position:'absolute',left:0,top:'3px',bottom:'3px',width:geom(r).pc(r.value),borderRadius:'2px',background:geom(r).bar,transition:'width var(--dur) var(--ease-out)'}"/>
29
29
  <div :style="{position:'absolute',left:geom(r).pc(r.target),top:0,bottom:0,width:'2px',background:'var(--chart-total)'}"/>
@@ -38,7 +38,7 @@ function move(e){
38
38
  <template v-if="axes">
39
39
  <g v-for="t in g.ticks" :key="t">
40
40
  <line :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sy(t)" :y2="g.sy(t)" :stroke="t===0?'var(--chart-axis)':'var(--chart-grid)'" stroke-width="1"/>
41
- <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-mono);font-size:var(--text-2xs);fill:var(--chart-label)">{{ valueFormat(t) }}</text>
41
+ <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ valueFormat(t) }}</text>
42
42
  </g>
43
43
  </template>
44
44
  <rect v-if="hover!=null" :x="g.pad.l+g.slot*hover" :y="g.pad.t" :width="g.slot" :height="height-g.pad.t-g.axisB" fill="var(--bg-hover)"/>
@@ -53,8 +53,8 @@ function move(e){
53
53
  </svg>
54
54
  <div v-if="hover!=null && interactive" :style="{position:'absolute',left:g.cx(hover)+'px',top:(height/2-20)+'px',transform:g.cx(hover)>width*0.62?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',lineHeight:'normal',whiteSpace:'nowrap'}">
55
55
  <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ candles[hover].t }}</div>
56
- <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">O {{ valueFormat(candles[hover].o) }} · H {{ valueFormat(candles[hover].h) }} · L {{ valueFormat(candles[hover].l) }} · C {{ valueFormat(candles[hover].c) }}</div>
57
- <div :style="{fontFamily:'var(--font-mono)',fontSize:'var(--text-2xs)',color:candles[hover].c>=candles[hover].o?'var(--chart-pos)':'var(--chart-neg)'}">
56
+ <div style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading)">O {{ valueFormat(candles[hover].o) }} · H {{ valueFormat(candles[hover].h) }} · L {{ valueFormat(candles[hover].l) }} · C {{ valueFormat(candles[hover].c) }}</div>
57
+ <div :style="{fontFamily: 'var(--font-sans)', fontVariantNumeric: 'tabular-nums',fontSize:'var(--text-2xs-mono)',color:candles[hover].c>=candles[hover].o?'var(--chart-pos)':'var(--chart-neg)'}">
58
58
  {{ (candles[hover].c-candles[hover].o>=0?'+':'')+(((candles[hover].c-candles[hover].o)/(candles[hover].o||1))*100).toFixed(2) }}%<span v-if="candles[hover].v!=null" style="color:var(--text-muted)"> · vol {{ fmtNum(candles[hover].v) }}</span>
59
59
  </div>
60
60
  </div>
@@ -1,7 +1,8 @@
1
1
  import React from "react";
2
2
  import { fmtNum, seriesColor } from "./chart-geometry.js";
3
3
 
4
- const sans = "var(--font-sans)", mono = "var(--font-mono)";
4
+ /* Chart numbers use the UI font with tabular figures; the monospace font is reserved for code. */
5
+ const sans = "var(--font-sans)", num = { fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums" };
5
6
 
6
7
  /** Legend row. items: [{label,color}] — with onToggle, clicking mutes a series. */
7
8
  export function ChartLegend({ items = [], hidden = [], onToggle, align = "left", style }) {
@@ -31,7 +32,7 @@ export function ChartTooltip({ x, y, title, rows = [], flip }) {
31
32
  <div key={i} style={{ display: "flex", alignItems: "center", gap: 6, padding: "1px 0" }}>
32
33
  {r.color ? <span style={{ width: 7, height: 7, borderRadius: 2, background: r.color, flexShrink: 0 }} /> : null}
33
34
  <span style={{ fontFamily: sans, fontSize: "var(--text-xs)", color: "var(--text-secondary)", whiteSpace: "nowrap" }}>{r.label}</span>
34
- <span style={{ fontFamily: mono, fontSize: "var(--text-xs)", color: "var(--text-heading)", marginLeft: "auto", paddingLeft: 10 }}>{r.value}</span>
35
+ <span style={{ ...num, fontSize: "var(--text-xs)", color: "var(--text-heading)", marginLeft: "auto", paddingLeft: 10 }}>{r.value}</span>
35
36
  </div>
36
37
  ))}
37
38
  </div>
@@ -45,7 +46,7 @@ export function PlotGrid({ w, h, pad, ticks, sy, xLabels = [], sxLabel, showAxis
45
46
  {ticks.map((t) => (
46
47
  <g key={t}>
47
48
  <line x1={pad.l} x2={w - pad.r} y1={sy(t)} y2={sy(t)} stroke={t === 0 ? "var(--chart-axis)" : "var(--chart-grid)"} strokeWidth="1" />
48
- <text x={pad.l - 8} y={sy(t) + 3} textAnchor="end" style={{ fontFamily: mono, fontSize: "var(--text-2xs)", fill: "var(--chart-label)" }}>{yFormat(t)}</text>
49
+ <text x={pad.l - 8} y={sy(t) + 3} textAnchor="end" style={{ ...num, fontSize: "var(--text-2xs-mono)", fill: "var(--chart-label)" }}>{yFormat(t)}</text>
49
50
  </g>
50
51
  ))}
51
52
  {showAxisLine ? <line x1={pad.l} x2={pad.l} y1={pad.t} y2={h - pad.b} stroke="var(--chart-axis)" strokeWidth="1" /> : null}
@@ -10,7 +10,7 @@ defineProps({ x: [Number, String], y: [Number, String], title: [String, Number],
10
10
  <div v-for="(r, i) in rows" :key="i" style="display:flex;align-items:center;gap:6px;padding:1px 0">
11
11
  <span v-if="r.color" :style="{ width: '7px', height: '7px', borderRadius: '2px', background: r.color, flexShrink: 0 }"></span>
12
12
  <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary);white-space:nowrap">{{ r.label }}</span>
13
- <span style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading);margin-left:auto;padding-left:10px">{{ r.value }}</span>
13
+ <span style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading);margin-left:auto;padding-left:10px">{{ r.value }}</span>
14
14
  </div>
15
15
  </div>
16
16
  </template>
@@ -44,7 +44,7 @@ export function ChordChart({ labels = [], matrix = [], size = 380, thickness = 1
44
44
  style={{ cursor: interactive ? "default" : undefined }}>
45
45
  <path d={arcPath(cx, cy, rO, rI, a.a0, a.a1)} fill={colorOf(a.i)} opacity={hover == null || on ? 1 : 0.4} style={{ transition: "opacity var(--dur-fast) var(--ease-out)" }} />
46
46
  <text x={lx} y={ly + 3} textAnchor={anchor} style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-2xs)", fill: on ? "var(--text-heading)" : "var(--text-secondary)" }}>{labels[a.i]}</text>
47
- <text x={lx} y={ly + 14} textAnchor={anchor} style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", fill: "var(--chart-label)" }}>{valueFormat(a.value)}</text>
47
+ <text x={lx} y={ly + 14} textAnchor={anchor} style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", fill: "var(--chart-label)" }}>{valueFormat(a.value)}</text>
48
48
  </g>
49
49
  );
50
50
  })}
@@ -43,12 +43,12 @@ const tip = computed(()=>{
43
43
  <g v-for="a in g.arcs" :key="a.i" @mouseenter="interactive && (hover={kind:'node',i:a.i})" @mouseleave="interactive && (hover=null)">
44
44
  <path :d="a.d" :fill="a.color" :opacity="hover==null||(hover.kind==='node'&&hover.i===a.i)?1:0.4" style="transition:opacity var(--dur-fast) var(--ease-out)"/>
45
45
  <text :x="a.lx" :y="a.ly+3" :text-anchor="a.anchor" style="font-family:var(--font-sans);font-size:var(--text-2xs);fill:var(--text-secondary)">{{ labels[a.i] }}</text>
46
- <text :x="a.lx" :y="a.ly+14" :text-anchor="a.anchor" style="font-family:var(--font-mono);font-size:var(--text-2xs);fill:var(--chart-label)">{{ valueFormat(a.value) }}</text>
46
+ <text :x="a.lx" :y="a.ly+14" :text-anchor="a.anchor" style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-2xs-mono);fill:var(--chart-label)">{{ valueFormat(a.value) }}</text>
47
47
  </g>
48
48
  </svg>
49
49
  <div v-if="tip && interactive" :style="{position:'absolute',left:tip.x+'px',top:tip.y+'px',transform:tip.x>size*0.55?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',lineHeight:'normal',whiteSpace:'nowrap'}">
50
50
  <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ tip.title }}</div>
51
- <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ tip.value }}</div>
51
+ <div style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading)">{{ tip.value }}</div>
52
52
  </div>
53
53
  </div>
54
54
  </template>
@@ -62,7 +62,7 @@ export function DonutChart({ segments = [], size = 180, thickness = 22, pie = fa
62
62
  style={{ display: "inline-flex", alignItems: "center", gap: 7, cursor: interactive ? "default" : undefined, opacity: hover == null || hover === i ? 1 : 0.45, transition: "var(--t-hover)" }}>
63
63
  <span style={{ width: 8, height: 8, borderRadius: 2, background: colorOf(s, i), flexShrink: 0 }} />
64
64
  <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", color: "var(--text-secondary)" }}>{s.label}</span>
65
- <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)", color: "var(--text-heading)", marginLeft: 6 }}>{((s.value / total) * 100).toFixed(0)}%</span>
65
+ <span style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-xs)", color: "var(--text-heading)", marginLeft: 6 }}>{((s.value / total) * 100).toFixed(0)}%</span>
66
66
  </span>
67
67
  ))}
68
68
  </div>
@@ -45,7 +45,7 @@ const tipStyle = computed(() => {
45
45
  </div>
46
46
  <div v-if="hover!=null && interactive" :style="tipStyle" >
47
47
  <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px;white-space:nowrap">{{ segments[hover].label }}</div>
48
- <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading);white-space:nowrap">{{ valueFormat(segments[hover].value) }} · {{ ((segments[hover].value/total)*100).toFixed(1) }}%</div>
48
+ <div style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading);white-space:nowrap">{{ valueFormat(segments[hover].value) }} · {{ ((segments[hover].value/total)*100).toFixed(1) }}%</div>
49
49
  </div>
50
50
  </div>
51
51
  <div v-if="legend" style="display:flex;flex-direction:column;gap:6px">
@@ -53,7 +53,7 @@ const tipStyle = computed(() => {
53
53
  :style="{display:'inline-flex',alignItems:'center',gap:'7px',opacity:hover==null||hover===a.i?1:.45,transition:'var(--t-hover)'}">
54
54
  <span :style="{width:'8px',height:'8px',borderRadius:'2px',background:a.color,flexShrink:0}"/>
55
55
  <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary)">{{ a.s.label }}</span>
56
- <span style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading);margin-left:6px">{{ ((a.s.value/total)*100).toFixed(0) }}%</span>
56
+ <span style="font-family:var(--font-sans);font-variant-numeric:tabular-nums;font-size:var(--text-xs);color:var(--text-heading);margin-left:6px">{{ ((a.s.value/total)*100).toFixed(0) }}%</span>
57
57
  </span>
58
58
  </div>
59
59
  </div>
@@ -20,12 +20,12 @@ export function FunnelChart({ stages = [], width = 420, bandHeight = 34, gap = 1
20
20
  <div style={{ display: "flex", justifyContent: "center" }}>
21
21
  <div style={{ width: Math.max(12, share * 100) + "%", height: bandHeight, borderRadius: "var(--radius-sm)", background: "var(--series-1)", opacity: 1 - i * (0.55 / Math.max(1, stages.length - 1)), display: "flex", alignItems: "center", justifyContent: "center", gap: 8, minWidth: 150, transition: "width var(--dur) var(--ease-out)" }}>
22
22
  <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", color: "var(--on-accent)", whiteSpace: "nowrap" }}>{s.label}</span>
23
- <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)", color: "var(--on-accent)" }}>{valueFormat(s.value)}</span>
24
- {hover === i ? <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", color: "var(--on-accent)", opacity: 0.8 }}>{((share) * 100).toFixed(0)}% of top</span> : null}
23
+ <span style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-xs)", color: "var(--on-accent)" }}>{valueFormat(s.value)}</span>
24
+ {hover === i ? <span style={{ fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", color: "var(--on-accent)", opacity: 0.8 }}>{((share) * 100).toFixed(0)}% of top</span> : null}
25
25
  </div>
26
26
  </div>
27
27
  {prev != null ? (
28
- <div style={{ textAlign: "center", paddingTop: 3, fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", color: prev < 0.5 ? "var(--chart-neg)" : "var(--text-muted)" }}>
28
+ <div style={{ textAlign: "center", paddingTop: 3, fontFamily: "var(--font-sans)", fontVariantNumeric: "tabular-nums", fontSize: "var(--text-2xs-mono)", color: prev < 0.5 ? "var(--chart-neg)" : "var(--text-muted)" }}>
29
29
  ↓ {(prev * 100).toFixed(0)}%
30
30
  </div>
31
31
  ) : null}