@jeffusion/bungee-core 6.10.1 → 6.11.1

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.
@@ -2,19 +2,17 @@
2
2
  import { isLoading, locale } from 'svelte-i18n';
3
3
  import { getPluginText } from '$utils/plugin-i18n';
4
4
  import * as Tooltip from '$components/ui/tooltip';
5
- import { Bar, ChartJS, BarElement, CategoryScale, LinearScale, Tooltip as ChartTooltip, _, formatCompactNumber, type ChartData, type ChartOptions } from '@bungee/plugin-sdk';
6
- import type { Plugin, TooltipModel } from 'chart.js';
7
- import { buildTimeSeries, formatTokenCount, modelColorIndex, OTHER_MODEL, timeAxisLabels, type TimeBucket, type TokenStatsRange } from './labels';
5
+ import { BarChart, _, barSeriesColor, formatCompactNumber, type ChartData, type ChartOptions } from '@bungee/plugin-sdk';
6
+ import type { Chart, Plugin, TooltipModel } from 'chart.js';
7
+ import { buildTimeSeries, formatTokenCount, OTHER_MODEL, timeAxisLabels, type TimeBucket, type TokenStatsRange } from './labels';
8
8
  import type { StatsResponse } from './stats-resource';
9
-
10
- ChartJS.register(BarElement, CategoryScale, LinearScale, ChartTooltip);
11
9
  let { stats, range, refreshedAt, pluginName = 'token-stats', presentation = 'dashboard' }:
12
10
  { stats: StatsResponse; range: TokenStatsRange; refreshedAt: number; pluginName?: string; presentation?: 'dashboard' | 'page' } = $props();
13
11
  const pageChart = $derived(presentation === 'page');
14
12
  const t = (key: string) => $isLoading ? '' : getPluginText(key, pluginName, (id, options) => $_(id, options));
15
13
  const display = formatCompactNumber;
16
14
  const fullCount = (value: unknown) => formatTokenCount(value, $locale ?? undefined);
17
- let chart = $state.raw<ChartJS<'bar'> | null>(null);
15
+ let chart = $state.raw<Chart<'bar'> | null>(null);
18
16
  let bucketTriggers: HTMLDivElement | undefined = $state();
19
17
  const trend = $derived(buildTimeSeries(stats.data, range, stats.bucketMs ?? 0, stats.asOfMs ?? refreshedAt, stats.bucketStarts));
20
18
  const dayBuckets = $derived(['7d', '30d', 'week', 'month'].includes(range));
@@ -34,15 +32,20 @@
34
32
  : Math.max(1, Math.ceil((trend.buckets.length - 1) / Math.max(1, Math.floor(plotWidth / (dayBuckets ? 56 : 110)) - 1))));
35
33
  const showTick = (index: number) => index === 0 || index === trend.buckets.length - 1
36
34
  || index % tickStep === 0 && index <= trend.buckets.length - 1 - tickStep;
37
- const tones = ['bg-nexus-500', 'bg-sky-400', 'bg-emerald-400', 'bg-amber-400', 'bg-red-400'] as const;
38
- // Match the original Tailwind palette, including carbon-500 and zinc-500.
39
- const palette = ['#f97316', '#38bdf8', '#34d399', '#fbbf24', '#f87171'];
35
+ const OTHER_COLOR = '#373d4a';
36
+ const UNKNOWN_COLOR = '#71717a';
37
+ const modelColors = $derived(new Map(trend.models.map((id, index) => [id,
38
+ barSeriesColor(index, id === OTHER_MODEL ? OTHER_COLOR : id === 'unknown' ? UNKNOWN_COLOR : undefined)])));
39
+ const seriesColor = (id: string) =>
40
+ modelColors.get(id) ?? modelColors.get(OTHER_MODEL) ?? OTHER_COLOR;
40
41
  const data: ChartData<'bar'> = $derived({
41
42
  labels: trend.buckets.map(bucket => bucket.startMs),
42
43
  datasets: trend.models.map(id => ({
43
44
  label: modelLabel(id),
44
45
  data: trend.buckets.map(bucket => bucket.parts.find(part => part.id === id)?.tokens ?? 0),
45
- backgroundColor: id === OTHER_MODEL ? '#373d4a' : id === 'unknown' ? '#71717a' : palette[modelColorIndex(id)],
46
+ ...(id === OTHER_MODEL ? { backgroundColor: OTHER_COLOR }
47
+ : id === 'unknown' ? { backgroundColor: UNKNOWN_COLOR }
48
+ : {}),
46
49
  borderWidth: 0, borderRadius: 0, inflateAmount: 0,
47
50
  })),
48
51
  });
@@ -82,9 +85,6 @@
82
85
  function modelLabel(id: string): string {
83
86
  return id === OTHER_MODEL ? t('ui.otherModels') : id === 'unknown' ? t('ui.unknownModel') : id;
84
87
  }
85
- function modelTone(id: string): string {
86
- return id === OTHER_MODEL ? 'bg-carbon-500' : id === 'unknown' ? 'bg-zinc-500' : tones[modelColorIndex(id)];
87
- }
88
88
  function timeLabel(startMs: number, full = false): string {
89
89
  return new Intl.DateTimeFormat($locale ?? undefined, {
90
90
  ...(full ? { month: '2-digit' as const, day: '2-digit' as const, timeZoneName: 'shortOffset' as const } : {}),
@@ -154,7 +154,7 @@
154
154
  <div class="flex min-h-0 min-w-0 flex-1 flex-col">
155
155
  <div class="relative min-h-0 flex-1 border-b border-carbon-500">
156
156
  <div class="pointer-events-none absolute inset-y-0 z-10 {pageChart ? 'inset-x-0' : '-left-[6px] -right-[6px]'}">
157
- <Bar {data} {options} plugins={chartPlugins} bind:chart aria-hidden="true" />
157
+ <BarChart {data} {options} plugins={chartPlugins} bind:chart aria-hidden="true" />
158
158
  </div>
159
159
  <div class="h-full {pageChart ? 'grid' : 'flex items-end gap-[12px]'}" style:grid-template-columns={pageChart ? `repeat(${trend.buckets.length}, minmax(0, 1fr))` : undefined}
160
160
  bind:this={bucketTriggers} role="group" aria-label={t('ui.timeChart')}>
@@ -221,7 +221,7 @@
221
221
  {#each tooltipBucket.details as part (part.id)}
222
222
  <div class="flex items-start justify-between gap-3">
223
223
  <span class="flex min-w-0 items-start gap-1.5">
224
- <span class="mt-1 h-2 w-2 shrink-0 {modelTone(part.id)}" aria-hidden="true"></span>
224
+ <span class="mt-1 h-2 w-2 shrink-0" style:background={seriesColor(part.id)} aria-hidden="true"></span>
225
225
  <span class="break-all">{modelLabel(part.id)}</span>
226
226
  </span>
227
227
  <strong class="shrink-0 font-normal text-zinc-50" title={fullCount(part.tokens)} aria-label={fullCount(part.tokens)}>{display(part.tokens)}</strong>
@@ -235,7 +235,7 @@
235
235
  <div class="mt-2 flex shrink-0 flex-wrap gap-x-3 gap-y-1 font-mono {pageChart ? 'text-xs' : 'text-[10px]'} text-zinc-300" data-testid="token-stats-chart-legend">
236
236
  {#each trend.models as id (id)}
237
237
  <span class="inline-flex min-w-0 items-center gap-1" title={modelLabel(id)}>
238
- <span class="h-2 w-2 shrink-0 {modelTone(id)}" aria-hidden="true"></span>
238
+ <span class="h-2 w-2 shrink-0" style:background={seriesColor(id)} aria-hidden="true"></span>
239
239
  <span class={pageChart ? 'min-w-0 break-all' : 'max-w-28 truncate'}>{modelLabel(id)}</span>
240
240
  </span>
241
241
  {/each}
@@ -121,13 +121,6 @@ export function rankedModels(rows: readonly ModelUsageRow[]): RankedModel[] {
121
121
  }).sort((a, b) => (b.tokens ?? -1) - (a.tokens ?? -1) || a.id.localeCompare(b.id));
122
122
  }
123
123
 
124
- /** Stable across tabs and ranges; the actual palette is declared in the widget for Tailwind's scanner. */
125
- export function modelColorIndex(id: string): number {
126
- let hash = 2_166_136_261;
127
- for (let i = 0; i < id.length; i++) hash = Math.imul(hash ^ id.charCodeAt(i), 16_777_619);
128
- return (hash >>> 0) % 5;
129
- }
130
-
131
124
  /** Only counted tokens make a bar; empty buckets have no visible segments. */
132
125
  export function buildTimeSeries(rows: readonly ModelUsageRow[], range: TokenStatsRange, bucketMs: number, nowMs: number, bucketStarts?: readonly number[]): TimeSeries {
133
126
  if (!Number.isSafeInteger(bucketMs) || bucketMs <= 0) return { buckets: [], models: [], maxTokens: 0 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeffusion/bungee-core",
3
- "version": "6.10.1",
3
+ "version": "6.11.1",
4
4
  "description": "Bungee reverse proxy core engine",
5
5
  "main": "./dist/main.js",
6
6
  "types": "./dist/main.d.ts",
@@ -16,7 +16,7 @@
16
16
  "@iconify-json/lucide": "^1.2.145",
17
17
  "@iconify-json/tabler": "^1.2.42",
18
18
  "@iconify/utils": "^3.1.7",
19
- "@jeffusion/bungee-types": "^6.10.1",
19
+ "@jeffusion/bungee-types": "^6.11.1",
20
20
  "@types/lodash-es": "^4.17.12",
21
21
  "@types/ws": "^8.18.1",
22
22
  "acorn": "^8.15.0",