@loworbitstudio/visor 1.27.0 → 1.29.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-09-23T03:16:40.729Z",
3
+ "generated_at": "2026-09-24T04:59:55.138Z",
4
4
  "components": {
5
5
  "accessibility-specimen": {
6
6
  "category": "specimen",
@@ -394,10 +394,12 @@
394
394
  "class-variance-authority"
395
395
  ],
396
396
  "tokens_used": [
397
+ "--badge-bg",
397
398
  "--badge-border",
398
399
  "--badge-font-family",
399
400
  "--badge-font-size",
400
401
  "--badge-font-weight",
402
+ "--badge-ink",
401
403
  "--badge-letter-spacing",
402
404
  "--badge-md-gap",
403
405
  "--badge-md-padding",
@@ -1034,7 +1036,8 @@
1034
1036
  "sm",
1035
1037
  "md",
1036
1038
  "lg",
1037
- "dlg"
1039
+ "dlg",
1040
+ "icon"
1038
1041
  ]
1039
1042
  },
1040
1043
  "props": [
@@ -1045,9 +1048,9 @@
1045
1048
  },
1046
1049
  {
1047
1050
  "name": "size",
1048
- "type": "'sm' | 'md' | 'lg' | 'dlg'",
1051
+ "type": "'sm' | 'md' | 'lg' | 'dlg' | 'icon'",
1049
1052
  "default": "md",
1050
- "description": "Standard sizes plus `dlg` — the compact dialog-footer button (dlg-btn) from the Animal dialog substrate (VI-620): 11px/600 text, tokenized ~8/12px padding, radius-lg. Its three intents reuse the variant axis — primary via variant=\"default\", ghost via variant=\"ghost\" (renders a hairline-outlined, secondary-ink face at dlg size), danger via variant=\"destructive\" (renders a subtle-danger face at dlg size).\n"
1053
+ "description": "Standard sizes plus `dlg` — the compact dialog-footer button (dlg-btn) from the Animal dialog substrate (VI-620): 11px/600 text, tokenized ~8/12px padding, radius-lg. Its three intents reuse the variant axis — primary via variant=\"default\", ghost via variant=\"ghost\" (renders a hairline-outlined, secondary-ink face at dlg size), danger via variant=\"destructive\" (renders a subtle-danger face at dlg size). `icon` is a square icon-only mark — glyph (--button-icon-size) plus a hit-target pad (--button-icon-pad) on every side, so width always equals height. Use it with variant=\"ghost\" for a bare mark in secondary ink. Requires an aria-label; warns in development without one.\n"
1051
1054
  },
1052
1055
  {
1053
1056
  "name": "asChild",
@@ -1105,6 +1108,32 @@
1105
1108
  "--border-default",
1106
1109
  "--border-focus",
1107
1110
  "--border-strong",
1111
+ "--button-dlg-font-size",
1112
+ "--button-dlg-letter-spacing",
1113
+ "--button-dlg-radius",
1114
+ "--button-dlg-text-transform",
1115
+ "--button-icon-ghost-color",
1116
+ "--button-icon-pad",
1117
+ "--button-icon-radius",
1118
+ "--button-icon-size",
1119
+ "--button-letter-spacing",
1120
+ "--button-lg-font-size",
1121
+ "--button-lg-letter-spacing",
1122
+ "--button-lg-radius",
1123
+ "--button-lg-text-transform",
1124
+ "--button-md-font-size",
1125
+ "--button-md-letter-spacing",
1126
+ "--button-md-radius",
1127
+ "--button-md-text-transform",
1128
+ "--button-radius",
1129
+ "--button-sm-font-size",
1130
+ "--button-sm-letter-spacing",
1131
+ "--button-sm-radius",
1132
+ "--button-sm-text-transform",
1133
+ "--button-text-transform",
1134
+ "--control-edge-color",
1135
+ "--control-edge-style",
1136
+ "--control-edge-width",
1108
1137
  "--destructive",
1109
1138
  "--focus-ring-offset",
1110
1139
  "--focus-ring-width",
@@ -1586,8 +1615,13 @@
1586
1615
  "--checkbox-bg-checked",
1587
1616
  "--checkbox-border",
1588
1617
  "--checkbox-border-checked",
1618
+ "--checkbox-edgeless-bg",
1589
1619
  "--checkbox-radius",
1590
1620
  "--checkbox-size",
1621
+ "--control-edge-color",
1622
+ "--control-edge-style",
1623
+ "--control-edge-width",
1624
+ "--control-state-edge-width",
1591
1625
  "--focus-ring-width",
1592
1626
  "--hairline-strong",
1593
1627
  "--interactive-primary-bg",
@@ -1767,6 +1801,9 @@
1767
1801
  "--chip-sm-padding-x",
1768
1802
  "--chip-text-color",
1769
1803
  "--chip-text-transform",
1804
+ "--control-edge-color",
1805
+ "--control-edge-style",
1806
+ "--control-edge-width",
1770
1807
  "--focus-ring-offset",
1771
1808
  "--focus-ring-width",
1772
1809
  "--font-body",
@@ -4100,6 +4137,10 @@
4100
4137
  ],
4101
4138
  "tokens_used": [
4102
4139
  "--border-default",
4140
+ "--control-drop-edge-style",
4141
+ "--control-drop-edge-width",
4142
+ "--control-edge-color",
4143
+ "--control-edge-width",
4103
4144
  "--empty-state-actions-gap",
4104
4145
  "--empty-state-bg",
4105
4146
  "--empty-state-border",
@@ -4384,6 +4425,10 @@
4384
4425
  "class-variance-authority"
4385
4426
  ],
4386
4427
  "tokens_used": [
4428
+ "--field-label-color",
4429
+ "--field-label-font-size",
4430
+ "--field-label-letter-spacing",
4431
+ "--field-label-text-transform",
4387
4432
  "--font-size-sm",
4388
4433
  "--font-size-xs",
4389
4434
  "--font-weight-medium",
@@ -4503,6 +4548,10 @@
4503
4548
  "--border-default",
4504
4549
  "--border-focus",
4505
4550
  "--border-strong",
4551
+ "--control-drop-edge-style",
4552
+ "--control-drop-edge-width",
4553
+ "--control-edge-color",
4554
+ "--control-edge-width",
4506
4555
  "--focus-ring-offset",
4507
4556
  "--focus-ring-width",
4508
4557
  "--font-size-sm",
@@ -5307,6 +5356,93 @@
5307
5356
  ],
5308
5357
  "example": "<Inline gap=\"sm\">\n <Button>Save</Button>\n <Button variant=\"secondary\">Cancel</Button>\n</Inline>\n\n<Inline gap=\"xs\" wrap>\n {tags.map(t => <Badge key={t}>{t}</Badge>)}\n</Inline>\n"
5309
5358
  },
5359
+ "inline-edit": {
5360
+ "category": "form",
5361
+ "description": "Inline-editable text — the text, then a pencil. A click or Enter turns it into an input in place, at the same size; Enter, blur or Tab commits, Escape restores, and clearing it brings back a muted default.",
5362
+ "when_to_use": [
5363
+ "A title the user can rename where it stands — a section heading, a photo set's name, a venue group, a rider's title",
5364
+ "A single-line value whose default should come back when the user clears it",
5365
+ "Renaming at the size of the heading being renamed, with no layout jump into an editing state"
5366
+ ],
5367
+ "when_not_to_use": [
5368
+ "Multi-line text — use Textarea",
5369
+ "Rich text — use a dedicated rich-text editor",
5370
+ "A labelled card tile with a save button and an AI action — use EditableBlock",
5371
+ "A field inside a form that is always editable — use Input with Field"
5372
+ ],
5373
+ "variants": {
5374
+ "state": {
5375
+ "rest": "The text (or the muted default), then the pencil. No edge.",
5376
+ "editing": "A single-line input in the text's place, at the same size, drawing the shared control edge."
5377
+ }
5378
+ },
5379
+ "props": [
5380
+ {
5381
+ "name": "value",
5382
+ "type": "string",
5383
+ "description": "The committed text. An empty string means no override, and `defaultValue` shows instead."
5384
+ },
5385
+ {
5386
+ "name": "defaultValue",
5387
+ "type": "string",
5388
+ "default": "\"\"",
5389
+ "description": "Shown, muted, while `value` is empty, and used as the input's placeholder."
5390
+ },
5391
+ {
5392
+ "name": "onCommit",
5393
+ "type": "(value: string) => void",
5394
+ "description": "Called with the trimmed text on Enter, blur or Tab, only when it differs from `value`. An empty string means \"restore the default\". The parent updates `value`."
5395
+ },
5396
+ {
5397
+ "name": "label",
5398
+ "type": "string",
5399
+ "description": "The field's name (e.g. \"Section title\"). Names the input, and the pencil as \"Edit {label}\"."
5400
+ },
5401
+ {
5402
+ "name": "editLabel",
5403
+ "type": "string",
5404
+ "description": "Accessible name of the pencil. Defaults to \"Edit {label}\"."
5405
+ },
5406
+ {
5407
+ "name": "as",
5408
+ "type": "'span' | 'div' | 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'",
5409
+ "default": "\"span\"",
5410
+ "description": "The element to render. Typography inherits from it, so a title keeps its heading's size and colour."
5411
+ },
5412
+ {
5413
+ "name": "defaultEditing",
5414
+ "type": "boolean",
5415
+ "default": "false",
5416
+ "description": "Opens in the editing state on mount, without moving focus."
5417
+ }
5418
+ ],
5419
+ "dependencies": [
5420
+ "@phosphor-icons/react",
5421
+ "@loworbitstudio/visor-core"
5422
+ ],
5423
+ "tokens_used": [
5424
+ "--border-default",
5425
+ "--border-focus",
5426
+ "--button-icon-ghost-color",
5427
+ "--button-icon-pad",
5428
+ "--button-icon-size",
5429
+ "--control-edge-color",
5430
+ "--control-edge-style",
5431
+ "--control-edge-width",
5432
+ "--control-state-edge-width",
5433
+ "--focus-ring-offset",
5434
+ "--focus-ring-width",
5435
+ "--motion-duration-150",
5436
+ "--motion-easing-default",
5437
+ "--radius-sm",
5438
+ "--spacing-1",
5439
+ "--spacing-2",
5440
+ "--text-primary",
5441
+ "--text-secondary",
5442
+ "--text-tertiary"
5443
+ ],
5444
+ "example": "const [title, setTitle] = React.useState(\"\")\n\n<InlineEdit\n as=\"h2\"\n label=\"Section title\"\n value={title}\n defaultValue=\"Track record\"\n onCommit={setTitle}\n/>\n"
5445
+ },
5310
5446
  "input": {
5311
5447
  "category": "form",
5312
5448
  "description": "A text input component with focus and validation states.",
@@ -5353,6 +5489,10 @@
5353
5489
  "--border-error",
5354
5490
  "--border-focus",
5355
5491
  "--border-strong",
5492
+ "--control-edge-color",
5493
+ "--control-edge-style",
5494
+ "--control-edge-width",
5495
+ "--control-state-edge-width",
5356
5496
  "--field-control-bg",
5357
5497
  "--focus-ring-offset",
5358
5498
  "--focus-ring-width",
@@ -7033,6 +7173,78 @@
7033
7173
  ],
7034
7174
  "example": "<RadiusScale steps={[\n { token: \"--radius-sm\", name: \"sm\", px: 2 },\n { token: \"--radius-md\", name: \"md\", px: 4 },\n { token: \"--radius-full\", name: \"full\", px: 9999 },\n]} />\n"
7035
7175
  },
7176
+ "save-status": {
7177
+ "category": "feedback",
7178
+ "description": "Autosave readout — Saved / Saving / Unsaved / Couldn't save in a fixed-width slot that never reflows its row, with a retry mark after a failed save. aria-live polite; draws no edge.",
7179
+ "when_to_use": [
7180
+ "Showing where an autosaved value stands, beside the section or field it saves",
7181
+ "Any editor with no Save button, driven by the useAutosave hook",
7182
+ "Offering a retry after a save failed, without a toast"
7183
+ ],
7184
+ "when_not_to_use": [
7185
+ "Confirming a one-off action the user just took — use SuccessFeedback or a toast",
7186
+ "A save/cancel footer model — use the admin editor patterns' footer buttons",
7187
+ "Conflicts between tabs or users — use ConflictBanner"
7188
+ ],
7189
+ "variants": {
7190
+ "status": {
7191
+ "saved": "Saved — muted, the resting state.",
7192
+ "saving": "Saving — a write is in flight.",
7193
+ "unsaved": "Unsaved — a change is waiting for the debounce.",
7194
+ "refused": "Couldn't save — validation refused the value or the write failed; shows a retry mark when onRetry is passed."
7195
+ }
7196
+ },
7197
+ "props": [
7198
+ {
7199
+ "name": "status",
7200
+ "type": "'saved' | 'saving' | 'unsaved' | 'refused'",
7201
+ "description": "Where the value stands — pass useAutosave(...).status."
7202
+ },
7203
+ {
7204
+ "name": "onRetry",
7205
+ "type": "() => void",
7206
+ "description": "Shows a retry mark while status is refused. Pass useAutosave(...).retry."
7207
+ },
7208
+ {
7209
+ "name": "labels",
7210
+ "type": "Partial<Record<'saved' | 'saving' | 'unsaved' | 'refused', string>>",
7211
+ "description": "Override any of the four labels, e.g. to translate them."
7212
+ },
7213
+ {
7214
+ "name": "retryLabel",
7215
+ "type": "string",
7216
+ "default": "\"Retry saving\"",
7217
+ "description": "Accessible name of the retry mark."
7218
+ }
7219
+ ],
7220
+ "dependencies": [
7221
+ "@phosphor-icons/react",
7222
+ "@loworbitstudio/visor-core"
7223
+ ],
7224
+ "tokens_used": [
7225
+ "--border-focus",
7226
+ "--button-icon-ghost-color",
7227
+ "--button-icon-pad",
7228
+ "--button-icon-size",
7229
+ "--focus-ring-offset",
7230
+ "--focus-ring-width",
7231
+ "--font-size-xs",
7232
+ "--motion-duration-150",
7233
+ "--motion-easing-default",
7234
+ "--radius-sm",
7235
+ "--save-status-font-family",
7236
+ "--save-status-letter-spacing",
7237
+ "--save-status-text-transform",
7238
+ "--save-status-width",
7239
+ "--spacing-1",
7240
+ "--spacing-2",
7241
+ "--text-error",
7242
+ "--text-primary",
7243
+ "--text-secondary",
7244
+ "--text-tertiary"
7245
+ ],
7246
+ "example": "const autosave = useAutosave(bio, saveBio)\n\n<SaveStatus status={autosave.status} onRetry={autosave.retry} />\n"
7247
+ },
7036
7248
  "score-indicator": {
7037
7249
  "category": "admin",
7038
7250
  "description": "Compact circular ring visualization for percentage or ratio metrics — health score, uptime, engagement, capacity.",
@@ -7595,6 +7807,10 @@
7595
7807
  "--border-error",
7596
7808
  "--border-focus",
7597
7809
  "--border-strong",
7810
+ "--control-edge-color",
7811
+ "--control-edge-style",
7812
+ "--control-edge-width",
7813
+ "--control-state-edge-width",
7598
7814
  "--field-control-bg",
7599
7815
  "--field-menu-bg",
7600
7816
  "--focus-ring-width",
@@ -9417,10 +9633,16 @@
9417
9633
  "@radix-ui/react-switch"
9418
9634
  ],
9419
9635
  "tokens_used": [
9636
+ "--_switch-knob-size",
9637
+ "--_switch-knob-start",
9638
+ "--_switch-pad",
9420
9639
  "--border-default",
9421
9640
  "--border-error",
9422
9641
  "--border-focus",
9423
9642
  "--border-strong",
9643
+ "--control-edge-style",
9644
+ "--control-edge-width",
9645
+ "--control-state-edge-width",
9424
9646
  "--focus-ring-width",
9425
9647
  "--interactive-primary-bg",
9426
9648
  "--motion-duration-150",
@@ -9428,7 +9650,11 @@
9428
9650
  "--primary",
9429
9651
  "--shadow-sm",
9430
9652
  "--surface-page",
9431
- "--switch-track-bg"
9653
+ "--switch-edge-color",
9654
+ "--switch-knob-inset",
9655
+ "--switch-track-bg",
9656
+ "--switch-track-height",
9657
+ "--switch-track-width"
9432
9658
  ],
9433
9659
  "example": "<div style={{ display: \"flex\", alignItems: \"center\", gap: \"0.5rem\" }}>\n <Switch id=\"notifications\" />\n <Label htmlFor=\"notifications\">Enable notifications</Label>\n</div>\n"
9434
9660
  },
@@ -9704,6 +9930,10 @@
9704
9930
  "--border-default",
9705
9931
  "--border-focus",
9706
9932
  "--border-strong",
9933
+ "--control-edge-color",
9934
+ "--control-edge-style",
9935
+ "--control-edge-width",
9936
+ "--control-state-edge-width",
9707
9937
  "--focus-ring-width",
9708
9938
  "--font-size-sm",
9709
9939
  "--font-size-xs",
@@ -9754,6 +9984,7 @@
9754
9984
  "primary",
9755
9985
  "secondary",
9756
9986
  "tertiary",
9987
+ "warning",
9757
9988
  "inherit"
9758
9989
  ],
9759
9990
  "leading": [
@@ -9783,7 +10014,7 @@
9783
10014
  },
9784
10015
  {
9785
10016
  "name": "color",
9786
- "type": "'primary' | 'secondary' | 'tertiary' | 'inherit'",
10017
+ "type": "'primary' | 'secondary' | 'tertiary' | 'warning' | 'inherit'",
9787
10018
  "default": "'primary'"
9788
10019
  },
9789
10020
  {
@@ -9811,9 +10042,25 @@
9811
10042
  "--line-height-relaxed",
9812
10043
  "--line-height-snug",
9813
10044
  "--line-height-tight",
10045
+ "--text-lg-font-size",
10046
+ "--text-lg-letter-spacing",
10047
+ "--text-lg-text-transform",
10048
+ "--text-md-font-size",
10049
+ "--text-md-letter-spacing",
10050
+ "--text-md-text-transform",
9814
10051
  "--text-primary",
9815
10052
  "--text-secondary",
9816
- "--text-tertiary"
10053
+ "--text-sm-font-size",
10054
+ "--text-sm-letter-spacing",
10055
+ "--text-sm-text-transform",
10056
+ "--text-tertiary",
10057
+ "--text-warning",
10058
+ "--text-xl-font-size",
10059
+ "--text-xl-letter-spacing",
10060
+ "--text-xl-text-transform",
10061
+ "--text-xs-font-size",
10062
+ "--text-xs-letter-spacing",
10063
+ "--text-xs-text-transform"
9817
10064
  ],
9818
10065
  "example": "<Text>Default body text paragraph.</Text>\n\n<Text size=\"sm\" color=\"secondary\">\n Helper text or caption.\n</Text>\n\n<Text as=\"span\" weight=\"semibold\" size=\"lg\">\n Inline emphasized text\n</Text>\n"
9819
10066
  },
@@ -9859,6 +10106,10 @@
9859
10106
  "--border-error",
9860
10107
  "--border-focus",
9861
10108
  "--border-strong",
10109
+ "--control-edge-color",
10110
+ "--control-edge-style",
10111
+ "--control-edge-width",
10112
+ "--control-state-edge-width",
9862
10113
  "--focus-ring-width",
9863
10114
  "--font-size-base",
9864
10115
  "--font-size-sm",
@@ -10031,6 +10282,74 @@
10031
10282
  ],
10032
10283
  "example": "<Timeline>\n <TimelineItem status=\"complete\">\n <TimelineContent>\n <TimelineTitle>Order placed</TimelineTitle>\n <TimelineTimestamp dateTime=\"2024-01-15\">Jan 15</TimelineTimestamp>\n </TimelineContent>\n </TimelineItem>\n <TimelineItem status=\"active\">\n <TimelineContent>\n <TimelineTitle>In transit</TimelineTitle>\n <TimelineDescription>Package is on the way.</TimelineDescription>\n </TimelineContent>\n </TimelineItem>\n <TimelineItem status=\"upcoming\">\n <TimelineContent>\n <TimelineTitle>Delivered</TimelineTitle>\n </TimelineContent>\n </TimelineItem>\n</Timeline>\n"
10033
10284
  },
10285
+ "tin-input": {
10286
+ "category": "form",
10287
+ "description": "A write-only, masked 9-digit TIN/EIN input with a last-four echo that never re-displays the value.",
10288
+ "when_to_use": [
10289
+ "Collecting a Social Security number or an EIN (a W-9, a payout or tax form)",
10290
+ "Any 9-digit taxpayer ID that is stored server-side and must never be shown back",
10291
+ "Showing a TIN already on file by its last four, with a way to replace it"
10292
+ ],
10293
+ "when_not_to_use": [
10294
+ "Passwords (use PasswordInput — it has a show/hide toggle, which a TIN must not)",
10295
+ "Verification codes (use OTPInput)",
10296
+ "Numbers the user needs to read back or edit in place (use Input or NumberInput)"
10297
+ ],
10298
+ "props": [
10299
+ {
10300
+ "name": "kind",
10301
+ "type": "'ssn' | 'ein'",
10302
+ "required": true,
10303
+ "description": "Display grouping — SSN"
10304
+ },
10305
+ {
10306
+ "name": "onValueChange",
10307
+ "type": "(digits: string | null) => void",
10308
+ "required": true,
10309
+ "description": "Called with the 9 digits once the entry is complete, and with null while it is incomplete or cleared."
10310
+ },
10311
+ {
10312
+ "name": "lastFour",
10313
+ "type": "string",
10314
+ "description": "Last four digits of a TIN already on file. Renders a read-only \"On file · ending 1234\" with a Replace button. A new value brings the echo back after a Replace. Only ever pass four digits."
10315
+ },
10316
+ {
10317
+ "name": "onReplace",
10318
+ "type": "() => void",
10319
+ "description": "Called when Replace swaps the on-file echo for an empty field."
10320
+ },
10321
+ {
10322
+ "name": "error",
10323
+ "type": "string",
10324
+ "description": "Error message, rendered as a FieldError and bound by aria-describedby; sets aria-invalid."
10325
+ },
10326
+ {
10327
+ "name": "size",
10328
+ "type": "'sm' | 'md' | 'lg'",
10329
+ "default": "md",
10330
+ "description": "Size of the field and the Replace button. lg meets 44px touch targets."
10331
+ },
10332
+ {
10333
+ "name": "disabled",
10334
+ "type": "boolean",
10335
+ "default": "false",
10336
+ "description": "Disables the field and the Replace button."
10337
+ },
10338
+ {
10339
+ "name": "id",
10340
+ "type": "string",
10341
+ "description": "Id for the input, so a FieldLabel htmlFor associates the label."
10342
+ }
10343
+ ],
10344
+ "dependencies": [
10345
+ "@loworbitstudio/visor-core"
10346
+ ],
10347
+ "tokens_used": [
10348
+ "--spacing-2",
10349
+ "--text-secondary"
10350
+ ],
10351
+ "example": "<Field>\n <FieldLabel htmlFor=\"tin\">Social Security number</FieldLabel>\n <TinInput id=\"tin\" kind=\"ssn\" onValueChange={setTin} />\n</Field>\n\n<TinInput id=\"tin\" kind=\"ein\" lastFour=\"6789\" onValueChange={setTin} />\n"
10352
+ },
10034
10353
  "toast": {
10035
10354
  "category": "feedback",
10036
10355
  "description": "Ephemeral notification messages that appear and auto-dismiss.",
@@ -12047,6 +12366,68 @@
12047
12366
  }
12048
12367
  ]
12049
12368
  },
12369
+ "use-autosave": {
12370
+ "description": "Autosave for one value: debounces the write (600ms), runs one write at a time so the newest value wins, flushes a pending change on pagehide, tab hide and unmount, and reports saved · saving · unsaved · refused with retry().",
12371
+ "params": [
12372
+ {
12373
+ "name": "value",
12374
+ "type": "T",
12375
+ "required": true,
12376
+ "description": "The value to keep saved. The first render's value counts as saved and is not written."
12377
+ },
12378
+ {
12379
+ "name": "write",
12380
+ "type": "(value: T, context: { leaving: boolean }) => unknown",
12381
+ "required": true,
12382
+ "description": "Persists the value. Return a promise; a rejection (or a throw) puts the hook in refused. `leaving` is true when the page is being left, so use `keepalive` or `sendBeacon`."
12383
+ },
12384
+ {
12385
+ "name": "options.delay",
12386
+ "type": "number",
12387
+ "required": false,
12388
+ "description": "Debounce in milliseconds after the last change. Defaults to 600."
12389
+ },
12390
+ {
12391
+ "name": "options.validate",
12392
+ "type": "(value: T) => boolean",
12393
+ "required": false,
12394
+ "description": "Return false to refuse a value. A refused value is never written, stays in the control, and puts the hook in refused."
12395
+ },
12396
+ {
12397
+ "name": "options.isEqual",
12398
+ "type": "(a: T, b: T) => boolean",
12399
+ "required": false,
12400
+ "description": "Decides whether a value is already saved. Defaults to Object.is; pass one for object values."
12401
+ }
12402
+ ],
12403
+ "returns": [
12404
+ {
12405
+ "name": "status",
12406
+ "type": "'saved' | 'saving' | 'unsaved' | 'refused'",
12407
+ "description": "Where the value stands. Pass it to SaveStatus."
12408
+ },
12409
+ {
12410
+ "name": "reason",
12411
+ "type": "'invalid' | 'failed' | null",
12412
+ "description": "Why the hook is refused — validate rejected the value, or write failed."
12413
+ },
12414
+ {
12415
+ "name": "error",
12416
+ "type": "unknown",
12417
+ "description": "What write threw or rejected with, while reason is failed."
12418
+ },
12419
+ {
12420
+ "name": "retry",
12421
+ "type": "() => void",
12422
+ "description": "Write the current value now."
12423
+ },
12424
+ {
12425
+ "name": "flush",
12426
+ "type": "() => void",
12427
+ "description": "Write a pending change now instead of waiting for the debounce."
12428
+ }
12429
+ ]
12430
+ },
12050
12431
  "use-click-outside": {
12051
12432
  "description": "Fires a callback when a click or touch event occurs outside the referenced element. Useful for closing dropdowns and popovers.",
12052
12433
  "params": [
@@ -12739,6 +13120,7 @@
12739
13120
  "offline-banner",
12740
13121
  "popover",
12741
13122
  "progress",
13123
+ "save-status",
12742
13124
  "skeleton",
12743
13125
  "slow-network-bar",
12744
13126
  "success-feedback",
@@ -12786,6 +13168,7 @@
12786
13168
  "file-upload",
12787
13169
  "form",
12788
13170
  "form-error",
13171
+ "inline-edit",
12789
13172
  "input",
12790
13173
  "label",
12791
13174
  "number-input",
@@ -12801,6 +13184,7 @@
12801
13184
  "switch",
12802
13185
  "tag-input",
12803
13186
  "textarea",
13187
+ "tin-input",
12804
13188
  "toggle-group"
12805
13189
  ],
12806
13190
  "media": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loworbitstudio/visor",
3
- "version": "1.27.0",
3
+ "version": "1.29.0",
4
4
  "description": "CLI for the Visor design system — add components, hooks, and utilities to your project.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -46,7 +46,7 @@
46
46
  "dependencies": {
47
47
  "@aws-sdk/client-s3": "^3.1021.0",
48
48
  "@babel/parser": "^7.26.0",
49
- "@loworbitstudio/visor-theme-engine": "^0.21.0",
49
+ "@loworbitstudio/visor-theme-engine": "^0.23.0",
50
50
  "commander": "^13.1.0",
51
51
  "diff": "^8.0.4",
52
52
  "picocolors": "^1.1.1",