@loworbitstudio/visor 1.31.0 → 1.35.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.4.0",
3
- "generated_at": "2026-10-03T23:23:41.697Z",
3
+ "generated_at": "2026-10-04T07:08:34.776Z",
4
4
  "components": {
5
5
  "accessibility-specimen": {
6
6
  "changeType": "current",
@@ -56,6 +56,12 @@
56
56
  "breakingChange": false,
57
57
  "migrationNote": null
58
58
  },
59
+ "bottom-nav": {
60
+ "changeType": "current",
61
+ "files": [],
62
+ "breakingChange": false,
63
+ "migrationNote": null
64
+ },
59
65
  "box": {
60
66
  "changeType": "current",
61
67
  "files": [],
@@ -494,6 +500,12 @@
494
500
  "breakingChange": false,
495
501
  "migrationNote": null
496
502
  },
503
+ "position-picker": {
504
+ "changeType": "current",
505
+ "files": [],
506
+ "breakingChange": false,
507
+ "migrationNote": null
508
+ },
497
509
  "progress": {
498
510
  "changeType": "current",
499
511
  "files": [],
@@ -560,6 +572,12 @@
560
572
  "breakingChange": false,
561
573
  "migrationNote": null
562
574
  },
575
+ "segmented-control": {
576
+ "changeType": "current",
577
+ "files": [],
578
+ "breakingChange": false,
579
+ "migrationNote": null
580
+ },
563
581
  "segmented-progress": {
564
582
  "changeType": "current",
565
583
  "files": [],
package/dist/index.js CHANGED
@@ -8760,6 +8760,9 @@ function spawnCommand(cwd, options) {
8760
8760
  import { existsSync as existsSync31, mkdirSync as mkdirSync14, readFileSync as readFileSync33 } from "fs";
8761
8761
  import { dirname as dirname15, isAbsolute as isAbsolute7, resolve as resolve21 } from "path";
8762
8762
  var ICON_X = `React.createElement("svg", { viewBox: "0 0 256 256", width: "1em", height: "1em", fill: "currentColor", "aria-hidden": true }, React.createElement("path", { d: "M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z" }))`;
8763
+ var navIcon = (shape) => `function (p) { return React.createElement("svg", { viewBox: "0 0 256 256", width: p.size || 24, height: p.size || 24, className: p.className, fill: p.weight === "fill" ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 16, strokeLinejoin: "round", "aria-hidden": true }, React.createElement("path", { d: ${JSON.stringify(shape)} })); }`;
8764
+ var NAV_SHAPES = ["M40 56h176v144H40z", "M128 32l96 176H32z", "M128 32a96 96 0 1 0 0 192a96 96 0 0 0 0-192z", "M48 64h160v128H48zM48 112h160", "M40 200V120M104 200V64M168 200V96M216 200V152", "M64 216V96l64-64l64 64v120z", "M128 40a40 40 0 1 0 0 80a40 40 0 0 0 0-80zM48 216c0-44 36-72 80-72s80 28 80 72z"];
8765
+ var NAV_ICONS = NAV_SHAPES.map(navIcon);
8763
8766
  var FIXTURES = {
8764
8767
  // VI-680: hairline consumers. `__mod` is the component module, so a fixture can
8765
8768
  // reach the compound parts a single export does not expose.
@@ -9114,7 +9117,57 @@ var FIXTURES = {
9114
9117
  // the edge tokens leave a default theme unchanged.
9115
9118
  input: {
9116
9119
  default: { export: "Input", interactiveTarget: "input", props: `{ placeholder: "Email address" }` },
9117
- invalid: { export: "Input", interactiveTarget: "input", props: `{ defaultValue: "not-an-email", "aria-invalid": true }` }
9120
+ invalid: { export: "Input", interactiveTarget: "input", props: `{ defaultValue: "not-an-email", "aria-invalid": true }` },
9121
+ // VI-662: fixed text inside the well, beside the value.
9122
+ suffix: { export: "Input", interactiveTarget: "input", props: `{ "aria-label": "Address", defaultValue: "lowtide", suffix: ".epk.pro" }` },
9123
+ prefix: { export: "Input", interactiveTarget: "input", props: `{ "aria-label": "Amount", inputMode: "decimal", defaultValue: "1,250.00", prefix: "\u20AC" }` },
9124
+ // Descenders and accents: a native input must never clip them (text-box trim does).
9125
+ descenders: { export: "Input", interactiveTarget: "input", props: `{ "aria-label": "Amount", defaultValue: "Typography jpq \xC9\xC5\xD1", prefix: "\u20AC", suffix: "jpy" }` },
9126
+ both: { export: "Input", interactiveTarget: "input", props: `{ "aria-label": "Rate", inputMode: "decimal", defaultValue: "85", prefix: "$", suffix: "/ hr" }` }
9127
+ },
9128
+ // VI-671: a rule, a word and a rule.
9129
+ separator: {
9130
+ default: { export: "Separator", props: `{ style: { width: 360 } }` },
9131
+ labelled: { export: "Separator", props: `{ label: "or", style: { width: 360 } }` }
9132
+ },
9133
+ "number-input": {
9134
+ default: { export: "NumberInput", interactiveTarget: "input", props: `{ "aria-label": "Quantity", defaultValue: 4, min: 0, max: 99 }` },
9135
+ prefix: { export: "NumberInput", interactiveTarget: "input", props: `{ "aria-label": "Fee", defaultValue: 150, prefix: "$" }` },
9136
+ descenders: { export: "NumberInput", interactiveTarget: "input", props: `{ "aria-label": "Fee", defaultValue: 1299, prefix: "\u20AC", suffix: "jpy" }` },
9137
+ suffix: { export: "NumberInput", interactiveTarget: "input", props: `{ "aria-label": "Hours", defaultValue: 2, suffix: "h" }` }
9138
+ },
9139
+ // VI-654: the calendar renders open on the month of `value` (November), not the
9140
+ // device's month; `align-end` shows the popover right-aligned to the trigger.
9141
+ "date-picker": {
9142
+ default: { export: "DatePicker", interactiveTarget: "button", props: `{ value: new Date(2026, 10, 14), defaultOpen: true }` },
9143
+ "align-end": { export: "DatePicker", interactiveTarget: "button", props: `{ value: new Date(2026, 10, 14), defaultOpen: true, align: "end", className: "w-full" }` }
9144
+ },
9145
+ // VI-670: invalid keeps the digits; trailing holds a working indicator or Resend.
9146
+ "otp-input": {
9147
+ default: { export: "OTPInput", interactiveTarget: "input", props: `{ value: "428" }` },
9148
+ invalid: { export: "OTPInput", interactiveTarget: "input", props: `{ value: "428519", invalid: true, errorMessage: "That code didn't work. Check it and try again." }` },
9149
+ trailing: { export: "OTPInput", interactiveTarget: "input", props: `{ value: "428519", trailing: React.createElement("button", { type: "button" }, "Resend") }` }
9150
+ },
9151
+ // VI-672: `default` is the grid on its own ground; `on-image` draws it over a busy
9152
+ // synthetic photograph (turbulence, one light half and one dark half) so each
9153
+ // target's scrim ring is checked against both. The picker is its own frame here:
9154
+ // `position: relative` and a size stand in for the positioned photo frame.
9155
+ "position-picker": {
9156
+ default: { export: "PositionPicker", interactiveTarget: '[role="radio"]', props: `{ "aria-label": "Focal point", defaultValue: { y: "center", x: "center" } }` },
9157
+ "on-image": {
9158
+ export: "PositionPicker",
9159
+ interactiveTarget: '[role="radio"]',
9160
+ props: `(function () {
9161
+ var svg = "<svg xmlns='http://www.w3.org/2000/svg' width='480' height='300'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.06' numOctaves='4' seed='7'/><feColorMatrix values='1.4 0 0 0 0.05 0 1.2 0 0 0.05 0 0 1.1 0 0.05 0 0 0 1 0'/></filter><rect width='480' height='300' fill='#d9c8a6'/><rect width='480' height='300' filter='url(#n)'/><rect x='0' y='0' width='240' height='300' fill='#f4efe4' opacity='0.55'/><rect x='240' y='0' width='240' height='300' fill='#0b0b14' opacity='0.6'/></svg>";
9162
+ return {
9163
+ variant: "on-image",
9164
+ "aria-label": "Focal point",
9165
+ defaultValue: { y: "top", x: "right" },
9166
+ style: { position: "relative", inset: "auto", width: 480, height: 300, backgroundImage: "url('data:image/svg+xml;utf8," + encodeURIComponent(svg).replace(/'/g, "%27") + "')", backgroundSize: "cover" },
9167
+ };
9168
+ })()`
9169
+ },
9170
+ thumbnail: { export: "PositionPicker", interactiveTarget: '[role="radio"]', props: `{ variant: "on-image", "aria-label": "Focal point", defaultValue: { y: "bottom", x: "left" }, style: { position: "relative", inset: "auto", width: 40, height: 28, background: "#667" } }` }
9118
9171
  },
9119
9172
  // VI-624: `default` is the closed field; the popover fixtures render open so the
9120
9173
  // two-column (24-hour) and three-column (12-hour) anatomy can be inspected.
@@ -9143,6 +9196,54 @@ var FIXTURES = {
9143
9196
  export: "Select",
9144
9197
  interactiveTarget: "button",
9145
9198
  props: `{ children: React.createElement(__mod.SelectTrigger, { variant: "borderless" }, React.createElement(__mod.SelectValue, { placeholder: "Pick a plan" })) }`
9199
+ },
9200
+ // VI-692: forced open so the portaled listbox is in the shot.
9201
+ open: {
9202
+ export: "Select",
9203
+ props: `{ open: true, defaultValue: "pro", children: [React.createElement(__mod.SelectTrigger, { key: "t" }, React.createElement(__mod.SelectValue, { placeholder: "Pick a plan" })), React.createElement(__mod.SelectContent, { key: "c" }, [React.createElement(__mod.SelectItem, { key: "a", value: "free" }, "Free"), React.createElement(__mod.SelectItem, { key: "b", value: "pro" }, "Pro"), React.createElement(__mod.SelectItem, { key: "d", value: "team" }, "Team")])] }`
9204
+ }
9205
+ },
9206
+ // VI-692: forced open so the portaled tip is in the shot.
9207
+ tooltip: {
9208
+ default: { export: "TooltipProvider", props: `{ children: React.createElement(__mod.Tooltip, { open: true }, [React.createElement(__mod.TooltipTrigger, { key: "t", asChild: true }, React.createElement("button", { type: "button" }, "Hover me")), React.createElement(__mod.TooltipContent, { key: "c" }, "Tooltip on the theme")]) }` }
9209
+ },
9210
+ // VI-683: an icon rail under an app bar (position="contained"), collapsed to
9211
+ // icons. Render with `--width 1024 --state focus` to open the first item's
9212
+ // tooltip (the sidebar is mobile at or below 768px, and mobile never shows
9213
+ // it); the second item wears the unread dot.
9214
+ sidebar: {
9215
+ default: {
9216
+ export: "SidebarProvider",
9217
+ interactiveTarget: '[data-sidebar="menu-button"]',
9218
+ props: `(function () {
9219
+ var h = React.createElement;
9220
+ var glyph = function (d) {
9221
+ return h("svg", { viewBox: "0 0 256 256", width: 20, height: 20, fill: "currentColor", "aria-hidden": true }, h("path", { d: d }));
9222
+ };
9223
+ var items = [
9224
+ ["Edit EPK", "M227.31,73.37,182.63,28.68a16,16,0,0,0-22.63,0L36.69,152A15.86,15.86,0,0,0,32,163.31V208a16,16,0,0,0,16,16H92.69A15.86,15.86,0,0,0,104,219.31L227.31,96a16,16,0,0,0,0-22.63Z"],
9225
+ ["Inbox, 3 unread", "M32,136v56a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V136l-32-72a16,16,0,0,0-14.6-9.5H78.6A16,16,0,0,0,64,64Z", true],
9226
+ ["Calendar", "M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32Z"],
9227
+ ];
9228
+ return {
9229
+ defaultOpen: false,
9230
+ style: { flexDirection: "column", minHeight: 360, width: 420 },
9231
+ children: [
9232
+ h("div", { key: "bar", style: { height: 48, display: "flex", alignItems: "center", padding: "0 16px", background: "var(--surface-card)", color: "var(--text-primary)", fontSize: 14 } }, "App bar"),
9233
+ h("div", { key: "body", style: { display: "flex", flex: 1 } }, [
9234
+ h(__mod.Sidebar, { key: "rail", collapsible: "icon", position: "contained" },
9235
+ h(__mod.SidebarMenu, null, items.map(function (it) {
9236
+ return h(__mod.SidebarMenuItem, { key: it[0] }, [
9237
+ h(__mod.SidebarMenuButton, { key: "b", asChild: true, tooltip: it[0] }, h("a", { href: "#" }, glyph(it[1]))),
9238
+ it[2] ? h(__mod.SidebarMenuBadge, { key: "d", "aria-hidden": true, style: { top: 4, right: 4, minWidth: 8, height: 8, padding: 0, borderRadius: "50%", background: "var(--text-primary)" } }) : null,
9239
+ ]);
9240
+ }))
9241
+ ),
9242
+ h(__mod.SidebarInset, { key: "main" }, h("div", { style: { padding: 16, fontSize: 14 } }, "Page content")),
9243
+ ]),
9244
+ ],
9245
+ };
9246
+ })()`
9146
9247
  }
9147
9248
  },
9148
9249
  checkbox: {
@@ -9265,13 +9366,75 @@ var FIXTURES = {
9265
9366
  unsaved: { export: "SaveStatus", props: `{ status: "unsaved" }` },
9266
9367
  "unsaved-dot": { export: "SaveStatus", props: `{ status: "unsaved", unsavedAs: "dot" }` },
9267
9368
  refused: { export: "SaveStatus", props: `{ status: "refused" }` },
9268
- "refused-retry": { export: "SaveStatus", interactiveTarget: "button", props: `{ status: "refused", onRetry: function () {} }` }
9369
+ "refused-retry": { export: "SaveStatus", interactiveTarget: "button", props: `{ status: "refused", onRetry: function () {} }` },
9370
+ dot: { export: "SaveStatus", props: `{ status: "saved", dot: true }` },
9371
+ "dot-unsaved": { export: "SaveStatus", props: `{ status: "unsaved", dot: true }` }
9372
+ },
9373
+ // VI-673: the phone foot bar. `fixed: false` lays it in the render frame instead of pinning it.
9374
+ "bottom-nav": {
9375
+ default: {
9376
+ export: "BottomNav",
9377
+ interactiveTarget: "a",
9378
+ props: `{ "aria-label": "Workspace", fixed: false, items: [{ label: "Edit", icon: ${NAV_ICONS[0]}, href: "#edit", active: true }, { label: "Inbox", icon: ${NAV_ICONS[1]}, href: "#inbox", mark: 3 }, { label: "Calendar", icon: ${NAV_ICONS[2]}, href: "#calendar" }, { label: "Invoices", icon: ${NAV_ICONS[3]}, href: "#invoices" }, { label: "Analytics", icon: ${NAV_ICONS[4]}, href: "#analytics" }, { label: "Team", icon: ${NAV_ICONS[6]}, href: "#team", group: "admin" }, { label: "Settings", icon: ${NAV_ICONS[5]}, href: "#settings", group: "admin" }] }`
9379
+ },
9380
+ labelled: {
9381
+ export: "BottomNav",
9382
+ props: `{ "aria-label": "Workspace", fixed: false, showLabels: true, items: [{ label: "Home", icon: ${NAV_ICONS[5]}, href: "#home", active: true }, { label: "Inbox", icon: ${NAV_ICONS[1]}, href: "#inbox", mark: true }, { label: "Settings", icon: ${NAV_ICONS[5]}, href: "#settings" }] }`
9383
+ }
9384
+ },
9385
+ // VI-615: the single-select pill group. `fullwidth` fills the render frame.
9386
+ "segmented-control": {
9387
+ default: {
9388
+ export: "SegmentedControl",
9389
+ interactiveTarget: "[data-slot=toggle-group-item]",
9390
+ props: `{ "aria-label": "Payment status", defaultValue: "paid", options: [{ value: "unpaid", label: "Unpaid" }, { value: "paid", label: "Paid" }, { value: "any", label: "Any" }] }`
9391
+ },
9392
+ fullwidth: {
9393
+ export: "SegmentedControl",
9394
+ props: `{ "aria-label": "Artist scope", fullWidth: true, defaultValue: "active", options: [{ value: "active", label: "Active" }, { value: "archived", label: "Archived" }, { value: "dnb", label: "Do-not-book" }] }`
9395
+ },
9396
+ "disabled-option": {
9397
+ export: "SegmentedControl",
9398
+ props: `{ "aria-label": "View", defaultValue: "day", options: [{ value: "day", label: "Day" }, { value: "week", label: "Week" }, { value: "month", label: "Month", disabled: true }] }`
9399
+ }
9400
+ },
9401
+ // VI-690: the single-select toggle group in both variants; `--state focus` drives the first item.
9402
+ "toggle-group": {
9403
+ default: {
9404
+ export: "ToggleGroup",
9405
+ interactiveTarget: "[data-slot=toggle-group-item]",
9406
+ props: `(function () {
9407
+ var h = React.createElement;
9408
+ return { type: "single", "aria-label": "Text alignment", defaultValue: "center", children: [
9409
+ h(__mod.ToggleGroupItem, { key: "l", value: "left" }, "Left"),
9410
+ h(__mod.ToggleGroupItem, { key: "c", value: "center" }, "Center"),
9411
+ h(__mod.ToggleGroupItem, { key: "r", value: "right" }, "Right"),
9412
+ ] };
9413
+ })()`
9414
+ },
9415
+ outline: {
9416
+ export: "ToggleGroup",
9417
+ interactiveTarget: "[data-slot=toggle-group-item]",
9418
+ props: `(function () {
9419
+ var h = React.createElement;
9420
+ return { type: "single", variant: "outline", "aria-label": "View", defaultValue: "week", children: [
9421
+ h(__mod.ToggleGroupItem, { key: "d", value: "day" }, "Day"),
9422
+ h(__mod.ToggleGroupItem, { key: "w", value: "week" }, "Week"),
9423
+ h(__mod.ToggleGroupItem, { key: "m", value: "month" }, "Month"),
9424
+ ] };
9425
+ })()`
9426
+ }
9269
9427
  },
9270
9428
  "tag-input": {
9271
9429
  default: {
9272
9430
  export: "TagInput",
9273
9431
  interactiveTarget: "input",
9274
9432
  props: `{ defaultValue: ["design", "tokens"], placeholder: "Add a tag" }`
9433
+ },
9434
+ slot: {
9435
+ export: "TagInput",
9436
+ interactiveTarget: "input",
9437
+ props: `{ defaultValue: ["design", "tokens"], placeholder: "Add a tag", entryAs: "slot" }`
9275
9438
  }
9276
9439
  },
9277
9440
  chip: {
@@ -9446,6 +9609,11 @@ ${opts.componentCss}
9446
9609
  </style>
9447
9610
  <style data-visor="harness">
9448
9611
  html, body { margin: 0; padding: 0; }
9612
+ body {
9613
+ color: var(--text-primary, #111827);
9614
+ /* Same token the nextjs adapter's origination binds to body (VI-616). */
9615
+ font-family: var(--font-body, var(--font-sans, system-ui, -apple-system, sans-serif));
9616
+ }
9449
9617
  #theme-scope {
9450
9618
  box-sizing: border-box;
9451
9619
  min-height: 100vh;
@@ -9454,15 +9622,12 @@ html, body { margin: 0; padding: 0; }
9454
9622
  align-items: center;
9455
9623
  justify-content: center;
9456
9624
  background: var(--surface-page, #ffffff);
9457
- color: var(--text-primary, #111827);
9458
- /* Same token the nextjs adapter's origination binds to body (VI-616). */
9459
- font-family: var(--font-body, var(--font-sans, system-ui, -apple-system, sans-serif));
9460
9625
  }
9461
9626
  #root { width: 100%; max-width: 420px; }
9462
9627
  </style>
9463
9628
  </head>
9464
- <body>
9465
- <div id="theme-scope" class="${opts.themeClass}">
9629
+ <body class="${opts.themeClass}">
9630
+ <div id="theme-scope">
9466
9631
  <div id="root"></div>
9467
9632
  </div>
9468
9633
  <script>
@@ -9629,11 +9794,12 @@ async function renderCommand(component, cwd, options) {
9629
9794
  probe = await page.evaluate(
9630
9795
  `(function () {
9631
9796
  var scope = document.getElementById("theme-scope");
9797
+ var body = document.body;
9632
9798
  var html = document.documentElement;
9633
9799
  var read = function (el, prop) {
9634
9800
  return getComputedStyle(el).getPropertyValue(prop).trim();
9635
9801
  };
9636
- var themed = read(scope, "--surface-card");
9802
+ var themed = read(body, "--surface-card");
9637
9803
  var base = read(html, "--surface-card");
9638
9804
  return {
9639
9805
  themedSurfaceCard: themed,