@vvedantb/ui 0.24.0 → 0.25.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.
package/README.md CHANGED
@@ -879,7 +879,7 @@ links under a separator.
879
879
  ### `Icon*` app icons
880
880
 
881
881
  Tabler-style plain app glyphs: `IconVibot`, `IconVhub`, `IconVmem`, `IconVerve`,
882
- `IconVelth` and `IconProjectV`. No background, no padding. `APP_ICONS[app]`
882
+ `IconVelth` and `IconProjectV`. No background. Each sits on an equalised square viewBox, so all apps look the same size. `APP_ICONS[app]`
883
883
  looks one up by `AppName`.
884
884
 
885
885
  ```tsx
@@ -888,24 +888,36 @@ import { IconVerve } from "@vvedantb/ui"
888
888
  <IconVerve size={24} color="currentColor" />
889
889
  ```
890
890
 
891
- `size` is the height (default 24) and the width follows the glyph. `color`
891
+ `size` sets both width and height (default 24; a string is any CSS length). `color`
892
892
  defaults to `currentColor`. `stroke` sets the stroke width in pixels for stroke
893
893
  glyphs (fill glyphs ignore it). `title` gives an accessible name. The ref goes
894
894
  to the `<svg>`.
895
895
 
896
+ `align?: "center" | "start"` (0.25.1, default `"center"`) sets where the glyph sits
897
+ in its equal-size square. `"start"` left-aligns the glyph's ink in the same square
898
+ (same scale, same `size` x `size` box), so labels line up in a vertical list.
899
+ `APP_MARK_VIEWBOX_START` holds the viewBoxes.
900
+
901
+ ```tsx
902
+ <IconVmem size={16} align="start" />
903
+ ```
904
+
896
905
  ### `AppMark` (deprecated)
897
906
 
898
907
  Deprecated in favour of the `Icon*` components for plain marks. Tiles stay here.
899
908
  Family app icons: `app` (`projectv`, `vibot`, `vhub`, `vmem`, `verve`, `velth`),
900
909
  `variant`, `size`, `title`.
901
910
 
902
- - `variant="mark"` (default) is the plain logo in `currentColor`. It is cropped
903
- tight to the glyph (stroke marks include half the stroke width) with no
904
- padding. `size` is the rendered height; the width follows the glyph's aspect
905
- ratio, so it sits level with a wordmark. A string `size` (for example `"2rem"`)
906
- sets both width and height to that value. `APP_MARK_VIEWBOX` holds the tight
907
- viewBox per app; `appMarkSvg()` and `APP_MARK_SVG` use it.
908
- - `variant="tile"` and `"tile-light"` keep the equalised, padded 1024 grid on a
911
+ - `variant="mark"` (default) is the plain logo in `currentColor`. It sits
912
+ on a square viewBox. The five apps (`vibot`, `vhub`, `vmem`, `verve`, `velth`) keep one
913
+ frame size, 629 x 629 (`APP_MARK_CROP_VIEWBOX` is the historical `173 197 629 629`), so relative
914
+ glyph sizes match the tiles. Each viewBox origin is centred on that glyph's `markBBox`, so no
915
+ glyph sits off-centre in its square. `projectv` is a brand mark, so it keeps its own 687 frame,
916
+ centred on its bbox (`PROJECTV_MARK_VIEWBOX`, `171.5 181 687 687`). `size` sets both width and
917
+ height; a string `size` (for example `"2rem"`) is any CSS length. `APP_MARK_VIEWBOX` holds the
918
+ viewBox for each app; `appMarkSvg()` and `APP_MARK_SVG` use it. `markAspect` returns
919
+ 1 and is kept for API stability only.
920
+ - `variant="tile"` and `"tile-light"` use the same 1024 grid on a
909
921
  rounded tile, always square. `appTileSvg`, `appAdaptiveSvg`, `appExtension128Svg`,
910
922
  `APP_TILE_SVG`, `APP_TILE_LIGHT_SVG` and favicons are unchanged.
911
923
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vvedantb/ui",
3
- "version": "0.24.0",
3
+ "version": "0.25.1",
4
4
  "license": "MIT",
5
5
  "description": "Shared React 19 UI for web apps: one JavaScript entry (primitives, shell pieces, app helpers including the Convex and Clerk provider, VITE env parsing and ensure-user, artifact viewers over the @vvedantb/server artifacts core, native tokens), design-token CSS, shared Tailwind v4 CSS and a Tailwind preset.",
6
6
  "type": "module",
@@ -1,8 +1,8 @@
1
1
  import { forwardRef, useId, type ForwardRefExoticComponent, type RefAttributes, type SVGProps } from "react";
2
- import { APP_MARKS, APP_MARK_VIEWBOX, APP_NAMES, markAspect, markLinecap, markStrokeWidth, type AppName } from "./svg";
2
+ import { APP_MARKS, APP_MARK_VIEWBOX, APP_MARK_VIEWBOX_START, APP_NAMES, markLinecap, markStrokeWidth, type AppName } from "./svg";
3
3
 
4
4
  export type AppIconProps = Omit<SVGProps<SVGSVGElement>, "children" | "height" | "width" | "viewBox" | "stroke" | "color"> & {
5
- /** Rendered height. Default 24. A number gives the width from the glyph's aspect ratio; a string sets width and height to the same value. */
5
+ /** Rendered width and height (the glyph is square). Default 24. A number is in pixels; a string is any CSS length. */
6
6
  size?: number | string;
7
7
  /** Glyph colour, for fill and stroke glyphs. Default `"currentColor"`. */
8
8
  color?: string;
@@ -10,24 +10,25 @@ export type AppIconProps = Omit<SVGProps<SVGSVGElement>, "children" | "height" |
10
10
  stroke?: number | string;
11
11
  /** Accessible name. Without it the icon is `aria-hidden`. */
12
12
  title?: string;
13
+ /** Where the glyph sits in its equal-size square. `"center"` (default) centres it. `"start"` left-aligns the glyph's ink in the same square, so labels line up in a vertical list. The rendered box stays `size` x `size`. */
14
+ align?: "center" | "start";
13
15
  };
14
16
 
15
17
  export type AppIconComponent = ForwardRefExoticComponent<AppIconProps & RefAttributes<SVGSVGElement>>;
16
18
 
17
- function viewBoxHeight(app: AppName): number {
19
+ function viewBoxSide(app: AppName): number {
18
20
  return Number(APP_MARK_VIEWBOX[app].split(" ")[3]);
19
21
  }
20
22
 
21
23
  function createAppIcon(app: AppName, displayName: string): AppIconComponent {
22
24
  const Icon = forwardRef<SVGSVGElement, AppIconProps>(function Icon(
23
- { size = 24, color = "currentColor", stroke, title, ...rest },
25
+ { size = 24, color = "currentColor", stroke, title, align = "center", ...rest },
24
26
  ref,
25
27
  ) {
26
28
  const titleId = useId();
27
29
  const { kind, paths } = APP_MARKS[app];
28
- const width = typeof size === "number" ? Math.round(size * markAspect(app) * 100) / 100 : size;
29
- const renderedHeight = typeof size === "number" ? size : 24;
30
- const strokeWidth = stroke === undefined ? markStrokeWidth(app) : Number(stroke) * (viewBoxHeight(app) / renderedHeight);
30
+ const renderedSize = typeof size === "number" ? size : 24;
31
+ const strokeWidth = stroke === undefined ? markStrokeWidth(app) : Number(stroke) * (viewBoxSide(app) / renderedSize);
31
32
  const glyphProps =
32
33
  kind === "fill"
33
34
  ? { fill: color }
@@ -37,11 +38,12 @@ function createAppIcon(app: AppName, displayName: string): AppIconComponent {
37
38
  {...rest}
38
39
  ref={ref}
39
40
  xmlns="http://www.w3.org/2000/svg"
40
- width={width}
41
+ width={size}
41
42
  height={size}
42
- viewBox={APP_MARK_VIEWBOX[app]}
43
+ viewBox={align === "start" ? APP_MARK_VIEWBOX_START[app] : APP_MARK_VIEWBOX[app]}
43
44
  preserveAspectRatio="xMidYMid meet"
44
45
  data-app={app}
46
+ data-align={align}
45
47
  role={title ? "img" : undefined}
46
48
  aria-hidden={title ? undefined : true}
47
49
  aria-labelledby={title ? titleId : undefined}
@@ -5,12 +5,10 @@ import { APP_MARKS, MARK_VIEWBOX, TILE_RADIUS, markLinecap, markStrokeWidth, typ
5
5
  export type AppMarkProps = Omit<SVGProps<SVGSVGElement>, "children" | "height" | "width" | "viewBox"> & {
6
6
  /** Which family app's mark to draw. */
7
7
  app: AppName;
8
- /** `"mark"` (default): plain logo in `currentColor`, cropped tight to the glyph with no padding. `"tile"`: white glyph on a black rounded tile. `"tile-light"`: black glyph on a white tile. */
8
+ /** `"mark"` (default): plain logo in `currentColor` on an equalised square viewBox, so every app looks the same size. `"tile"`: white glyph on a black rounded tile. `"tile-light"`: black glyph on a white tile. */
9
9
  variant?: AppMarkVariant;
10
10
  /**
11
- * Default 24. For `"mark"` it sets the rendered height and the width follows the glyph's aspect ratio
12
- * (a number gives an exact width; a string sets both to the same value and the glyph is centred).
13
- * Tiles are always square, `size` by `size`.
11
+ * Default 24. Sets both width and height, for marks and tiles alike (always square). A string is any CSS length.
14
12
  */
15
13
  size?: number | string;
16
14
  /** Accessible name. Without it the mark is `aria-hidden`. */
@@ -23,7 +21,7 @@ const TILE_TONES = {
23
21
  } as const;
24
22
 
25
23
  /**
26
- * One of the six family app marks. The plain mark is cropped tight; tiles use the padded 1024 grid.
24
+ * One of the six family app marks. The plain mark and the tiles share the same square 1024 viewBox, so a bare glyph matches the tile glyph optically.
27
25
  * @deprecated Use the per-app `Icon*` components (or `APP_ICONS[app]`) for plain marks. Tiles are for app and store icons only.
28
26
  */
29
27
  export function AppMark({ app, variant = "mark", size = 24, title, ...rest }: AppMarkProps) {
@@ -3,7 +3,10 @@ export { AppMark } from "./AppMark";
3
3
  export type { AppMarkProps } from "./AppMark";
4
4
  export {
5
5
  APP_MARKS,
6
+ APP_MARK_CROP_VIEWBOX,
6
7
  APP_MARK_VIEWBOX,
8
+ APP_MARK_VIEWBOX_START,
9
+ PROJECTV_MARK_VIEWBOX,
7
10
  APP_MARK_SVG,
8
11
  APP_NAMES,
9
12
  APP_TILE_LIGHT_SVG,
package/src/brand/svg.ts CHANGED
@@ -5,7 +5,7 @@ export const APP_NAMES = ["projectv", "vibot", "vhub", "vmem", "verve", "velth"]
5
5
 
6
6
  export type AppName = (typeof APP_NAMES)[number];
7
7
 
8
- /** `mark`: glyph only, cropped tight, follows `currentColor`. `tile`: white glyph on a black tile. `tile-light`: black glyph on a white tile. */
8
+ /** `mark`: glyph only on an equalised square viewBox, follows `currentColor`. `tile`: white glyph on a black tile. `tile-light`: black glyph on a white tile. */
9
9
  export type AppMarkVariant = "mark" | "tile" | "tile-light";
10
10
 
11
11
  export const MARK_VIEWBOX = "0 0 1024 1024";
@@ -175,20 +175,49 @@ export function markBBox(app: AppName): Box {
175
175
 
176
176
 
177
177
  /**
178
- * Tight viewBox per app (`minX minY width height` on the 1024 grid): the glyph box grown by half the
179
- * stroke width for stroke marks, rounded outward to whole units. Checked against `markBBox` in tests.
180
- * Used by the plain logo (`variant="mark"`, `appMarkSvg`). Tiles keep `MARK_VIEWBOX`.
178
+ * Frame size for the five app glyphs (`vibot`, `vhub`, `vmem`, `verve`, `velth`) in the plain logo (`variant="mark"`,
179
+ * `appMarkSvg`, `Icon*`): a 629 x 629 square, the union of their `markBBox` results on the 1024 tile grid
180
+ * (x 186 to 789, y 259.96 to 761.7) at side max(width, height) x 1.04 (about 4% padding), rounded outward.
181
+ * All five use this side, so glyph scale stays equal. Only the size is shared: each app has its own origin in
182
+ * `APP_MARK_VIEWBOX`. `projectv` is left out so its wide brand mark does not shrink the app glyphs.
183
+ */
184
+ export const APP_MARK_CROP_VIEWBOX = "173 197 629 629";
185
+
186
+ /**
187
+ * `projectv` (a brand mark, not an app) keeps a 687 x 687 frame, centred on `markBBox("projectv")`
188
+ * (x 172 to 858, y 377 to 672, centre 515 x 524.5), with no extra padding.
189
+ */
190
+ export const PROJECTV_MARK_VIEWBOX = "171.5 181 687 687";
191
+
192
+ /**
193
+ * Plain-mark viewBox per app. Always square. The five apps keep one 629 side (equal scale), and each origin is
194
+ * `markBBox(app)` centre minus 314.5 on both axes, so every glyph sits centred in its own frame. `projectv` uses
195
+ * its 687 frame, centre minus 343.5. Hardcoded; checked against `markBBox` in tests.
181
196
  */
182
197
  export const APP_MARK_VIEWBOX: Record<AppName, string> = {
183
- projectv: "172 377 686 295",
184
- vibot: "295 314 494 398",
185
- vhub: "186 338 582 348",
186
- vmem: "255 259 513 503",
187
- verve: "260 319 504 413",
188
- velth: "273 276 478 482",
198
+ projectv: PROJECTV_MARK_VIEWBOX,
199
+ vibot: "227.5 198.5 629 629",
200
+ vhub: "162.5 197.5 629 629",
201
+ vmem: "197.24772201231343 196.33100022273135 629 629",
202
+ verve: "197.5 210.9 629 629",
203
+ velth: "197.5 202.45 629 629",
204
+ };
205
+
206
+ /**
207
+ * Start-aligned viewBox per app, for `Icon*` `align="start"`. Same side and same y as `APP_MARK_VIEWBOX`; x is
208
+ * `markBBox(app)` minX, so the glyph's left ink edge sits flush with the frame's left edge. Hardcoded; checked
209
+ * against `markBBox` in tests.
210
+ */
211
+ export const APP_MARK_VIEWBOX_START: Record<AppName, string> = {
212
+ projectv: "172 181 687 687",
213
+ vibot: "295 198.5 629 629",
214
+ vhub: "186 197.5 629 629",
215
+ vmem: "255.5301217210191 196.33100022273135 629 629",
216
+ verve: "260.4 210.9 629 629",
217
+ velth: "273.9433593555699 202.45 629 629",
189
218
  };
190
219
 
191
- /** Width over height of a mark's tight viewBox. */
220
+ /** Width over height of a mark's viewBox. Always about 1, because the viewBoxes are square. Kept for API stability; do not use it for sizing. */
192
221
  export function markAspect(app: AppName): number {
193
222
  const [, , w = 1, h = 1] = APP_MARK_VIEWBOX[app].split(" ").map(Number);
194
223
  return w / h;
@@ -205,7 +234,7 @@ function pathElements(app: AppName): string {
205
234
  return APP_MARKS[app].paths.map((d) => `<path d="${d}"/>`).join("");
206
235
  }
207
236
 
208
- /** Glyph only, cropped tight with no padding, painted with `color` (default `currentColor`). */
237
+ /** Glyph only on its equalised square viewBox, painted with `color` (default `currentColor`). */
209
238
  export function appMarkSvg(app: AppName, color = "currentColor"): string {
210
239
  return `<svg ${NS} viewBox="${APP_MARK_VIEWBOX[app]}" ${markPaintAttrs(app, color)}>${pathElements(app)}</svg>\n`;
211
240
  }
@@ -11,9 +11,7 @@ export const V_PRO_APP_BENEFITS: readonly PricingPlanAppBenefit[] = [
11
11
  {
12
12
  app: "vibot",
13
13
  items: [
14
- "1000 messages / day",
15
- "100 image gens / day",
16
- "100 deep & 20 extreme searches / day",
14
+ "Higher AI limits/usage",
17
15
  "Smarter AI for chats, documents & sub-agents",
18
16
  ],
19
17
  },