@celestia-island/hikari 0.55.142 → 0.55.143

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celestia-island/hikari",
3
- "version": "0.55.142",
3
+ "version": "0.55.143",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Hikari Vue 3 component library — production-grade UI components based on shittim-chest design system",
@@ -48,6 +48,37 @@
48
48
  border-radius: var(--radius-sm, 4px);
49
49
  }
50
50
 
51
+ // Rate segment (the model-group capsule's `×N` tail, 2026-10-11): takes
52
+ // the inline-end rounding and provides the seam's divider from its own
53
+ // inline-start border; a success tint keeps it readable as a modifier,
54
+ // not a second name. With a rate present the NAME segment becomes a
55
+ // middle segment — square inline-end edge too (the join geometry below).
56
+ .s-model-tag-group[data-rate] .hk-badge.s-model-tag-name {
57
+ border-start-end-radius: 0;
58
+ border-end-end-radius: 0;
59
+ // The rate badge provides the seam's divider from its own
60
+ // inline-start border — the name's inline-end border must drop or
61
+ // the seam stacks ~2px (the num/name seam is 1px).
62
+ border-inline-end: none;
63
+ }
64
+
65
+ // Tagless + rate (the pill starts bare before the roster refines the
66
+ // id): the name is the FIRST child — its leading corners keep the
67
+ // capsule's rounding (only the :only-child escape covered it before).
68
+ .s-model-tag-group[data-rate] .hk-badge.s-model-tag-name:first-child {
69
+ border-start-start-radius: var(--radius-sm, 4px);
70
+ border-end-start-radius: var(--radius-sm, 4px);
71
+ }
72
+
73
+ .s-model-tag-group .hk-badge.s-model-tag-rate {
74
+ border-start-start-radius: 0;
75
+ border-end-start-radius: 0;
76
+ border-start-end-radius: var(--radius-sm, 4px);
77
+ border-end-end-radius: var(--radius-sm, 4px);
78
+ border-inline-start-color: rgb(var(--color-muted) / 35%);
79
+ flex-shrink: 0;
80
+ }
81
+
51
82
  .s-model-tag-name-text {
52
83
  display: block;
53
84
  overflow: hidden;
@@ -126,4 +126,30 @@ describe("HkModelTag", () => {
126
126
  c.querySelector(".s-model-tag-group")?.getAttribute("data-expanded"),
127
127
  ).toBeNull();
128
128
  });
129
+
130
+ it("renders the optional rate tail as the third joined segment", () => {
131
+ const c = mount(tagNode({ model: "gpt-5.5#2", rate: 1.5 }));
132
+ const group = c.querySelector(".s-model-tag-group");
133
+ expect(group?.hasAttribute("data-rate")).toBe(true);
134
+ expect(group?.querySelector(".s-model-tag-rate")?.textContent?.trim()).toBe("×1.5");
135
+ // The segments read in order: #2, name, rate.
136
+ expect(group?.textContent?.trim()).toBe("#2gpt-5.5×1.5");
137
+
138
+ // Trailing zeros trimmed, one decimal at most.
139
+ const whole = mount(tagNode({ model: "gpt-5.5#2", rate: 2 }));
140
+ expect(
141
+ whole.querySelector(".s-model-tag-rate")?.textContent?.trim(),
142
+ ).toBe("×2");
143
+ const tenth = mount(tagNode({ model: "gpt-5.5#2", rate: 0.1 }));
144
+ expect(
145
+ tenth.querySelector(".s-model-tag-rate")?.textContent?.trim(),
146
+ ).toBe("×0.1");
147
+ });
148
+
149
+ it("keeps the classic two-part pill untouched without a rate", () => {
150
+ const c = mount(tagNode({ model: "gpt-5.5#2" }));
151
+ const group = c.querySelector(".s-model-tag-group");
152
+ expect(group?.getAttribute("data-rate")).toBeNull();
153
+ expect(group?.querySelector(".s-model-tag-rate")).toBeNull();
154
+ });
129
155
  });
@@ -23,6 +23,10 @@ const ModelPill = defineComponent({
23
23
  model: { type: String, required: true },
24
24
  /** Render the name without truncation (used inside the hover card). */
25
25
  expanded: { type: Boolean, default: false },
26
+ /** Optional third segment — the billing rate tail (`×1.5`), as the
27
+ * model-group capsule's multiplier (2026-10-11 direction). Absent
28
+ * keeps the classic two-part pill untouched. */
29
+ rate: { type: Number, default: undefined },
26
30
  },
27
31
  setup(props) {
28
32
  // Split inside the render closure: capturing `splitModelId(props.model)`
@@ -32,10 +36,19 @@ const ModelPill = defineComponent({
32
36
  // stale, untagged pill.
33
37
  return () => {
34
38
  const { base, tag } = splitModelId(props.model);
39
+ // ×1.5 / ×2 / ×0.1 — trailing zeros trimmed, one decimal at most
40
+ // (the editor offers tenths; hundredths would crowd the pill).
41
+ // Number.isFinite guards the catalog edge: a NaN/Infinity leaking
42
+ // in would otherwise render "×NaN" (R1 F3).
43
+ const rateText =
44
+ props.rate != null && Number.isFinite(props.rate)
45
+ ? `×${Number(props.rate.toFixed(1)).toString()}`
46
+ : null;
35
47
  return (
36
48
  <span
37
49
  class="s-model-tag-group"
38
50
  data-expanded={props.expanded || undefined}
51
+ data-rate={rateText != null || undefined}
39
52
  >
40
53
  {tag && (
41
54
  <HBadge variant="primary" size="sm" mono pill={false} class="s-model-tag-num">
@@ -45,6 +58,11 @@ const ModelPill = defineComponent({
45
58
  <HBadge variant="muted" size="sm" mono pill={false} class="s-model-tag-name">
46
59
  <span class="s-model-tag-name-text">{base}</span>
47
60
  </HBadge>
61
+ {rateText != null && (
62
+ <HBadge variant="success" size="sm" mono pill={false} class="s-model-tag-rate">
63
+ {rateText}
64
+ </HBadge>
65
+ )}
48
66
  </span>
49
67
  );
50
68
  };
@@ -57,6 +75,8 @@ const ModelCard = defineComponent({
57
75
  props: {
58
76
  model: { type: String, required: true },
59
77
  meta: { type: Object as PropType<HModelMeta | undefined>, default: undefined },
78
+ /** Billing-rate tail passthrough (see ModelPill). */
79
+ rate: { type: Number, default: undefined },
60
80
  currency: { type: String, default: "$" },
61
81
  },
62
82
  setup(props) {
@@ -99,7 +119,7 @@ const ModelCard = defineComponent({
99
119
  return (
100
120
  <div class="s-model-card">
101
121
  <div class="s-model-card-title">
102
- <ModelPill model={props.model} expanded />
122
+ <ModelPill model={props.model} expanded rate={props.rate} />
103
123
  </div>
104
124
  {rows.length > 0 && (
105
125
  <div class="s-model-card-stats">
@@ -149,6 +169,8 @@ export const HkModelTag = defineComponent({
149
169
  /** Per-instance catalog override. */
150
170
  catalog: { type: Object as PropType<HModelCatalog | undefined>, default: undefined },
151
171
  currency: { type: String, default: "$" },
172
+ /** Optional billing-rate tail on the pill (`×1.5`); see ModelPill. */
173
+ rate: { type: Number, default: undefined },
152
174
  },
153
175
  setup(props) {
154
176
  const anchorRef = ref<HTMLElement | null>(null);
@@ -175,7 +197,7 @@ export const HkModelTag = defineComponent({
175
197
  onMouseenter={onEnter}
176
198
  onMouseleave={onLeave}
177
199
  >
178
- <ModelPill model={props.model} />
200
+ <ModelPill model={props.model} rate={props.rate} />
179
201
  <HPopover
180
202
  modelValue={open.value}
181
203
  onUpdate:modelValue={(v: boolean) => { open.value = v; }}