@nibapp/ui 0.0.0-stage → 0.1.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.
Files changed (292) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/LICENSE +21 -0
  3. package/README.md +124 -2
  4. package/TRADEMARKS.md +22 -0
  5. package/dist/_bundled/money/index.d.ts +98 -0
  6. package/dist/_bundled/money/index.js +152 -0
  7. package/dist/_bundled/tokens/index.d.ts +2 -0
  8. package/dist/_bundled/tokens/index.js +2 -0
  9. package/dist/_bundled/tokens/palette.gen.d.ts +51 -0
  10. package/dist/_bundled/tokens/palette.gen.js +206 -0
  11. package/dist/_bundled/tokens/spec.d.ts +291 -0
  12. package/dist/_bundled/tokens/spec.js +221 -0
  13. package/dist/components/accordion.d.ts +27 -0
  14. package/dist/components/accordion.js +66 -0
  15. package/dist/components/alert-dialog.d.ts +59 -0
  16. package/dist/components/alert-dialog.js +82 -0
  17. package/dist/components/alert.d.ts +141 -0
  18. package/dist/components/alert.js +55 -0
  19. package/dist/components/appear.d.ts +21 -0
  20. package/dist/components/appear.js +42 -0
  21. package/dist/components/avatar.d.ts +196 -0
  22. package/dist/components/avatar.js +135 -0
  23. package/dist/components/badge.d.ts +183 -0
  24. package/dist/components/badge.js +68 -0
  25. package/dist/components/block-stack.d.ts +33 -0
  26. package/dist/components/block-stack.js +197 -0
  27. package/dist/components/button.d.ts +327 -0
  28. package/dist/components/button.js +101 -0
  29. package/dist/components/card.d.ts +131 -0
  30. package/dist/components/card.js +100 -0
  31. package/dist/components/checkbox.d.ts +34 -0
  32. package/dist/components/checkbox.js +37 -0
  33. package/dist/components/choice-row.d.ts +104 -0
  34. package/dist/components/choice-row.js +56 -0
  35. package/dist/components/clamped-text.d.ts +17 -0
  36. package/dist/components/clamped-text.js +47 -0
  37. package/dist/components/code-block.d.ts +48 -0
  38. package/dist/components/code-block.js +124 -0
  39. package/dist/components/code-input.d.ts +49 -0
  40. package/dist/components/code-input.js +59 -0
  41. package/dist/components/combobox.d.ts +194 -0
  42. package/dist/components/combobox.js +176 -0
  43. package/dist/components/command-palette.d.ts +57 -0
  44. package/dist/components/command-palette.js +83 -0
  45. package/dist/components/context-menu.d.ts +24 -0
  46. package/dist/components/context-menu.js +30 -0
  47. package/dist/components/copy-button.d.ts +9 -0
  48. package/dist/components/copy-button.js +20 -0
  49. package/dist/components/copy-command.d.ts +18 -0
  50. package/dist/components/copy-command.js +19 -0
  51. package/dist/components/data-table.d.ts +240 -0
  52. package/dist/components/data-table.js +63 -0
  53. package/dist/components/dialog.d.ts +80 -0
  54. package/dist/components/dialog.js +85 -0
  55. package/dist/components/diff-lines.d.ts +20 -0
  56. package/dist/components/diff-lines.js +10 -0
  57. package/dist/components/diff-view.d.ts +128 -0
  58. package/dist/components/diff-view.js +675 -0
  59. package/dist/components/disclosure.d.ts +33 -0
  60. package/dist/components/disclosure.js +51 -0
  61. package/dist/components/drawn-check.d.ts +8 -0
  62. package/dist/components/drawn-check.js +16 -0
  63. package/dist/components/edge-resizer.d.ts +26 -0
  64. package/dist/components/edge-resizer.js +53 -0
  65. package/dist/components/empty-state.d.ts +112 -0
  66. package/dist/components/empty-state.js +56 -0
  67. package/dist/components/expand-toggle.d.ts +19 -0
  68. package/dist/components/expand-toggle.js +10 -0
  69. package/dist/components/field.d.ts +48 -0
  70. package/dist/components/field.js +60 -0
  71. package/dist/components/filter-chip.d.ts +47 -0
  72. package/dist/components/filter-chip.js +33 -0
  73. package/dist/components/group.d.ts +10 -0
  74. package/dist/components/group.js +9 -0
  75. package/dist/components/icon-text.d.ts +11 -0
  76. package/dist/components/icon-text.js +10 -0
  77. package/dist/components/image-sources.d.ts +57 -0
  78. package/dist/components/image-sources.js +58 -0
  79. package/dist/components/image-thumbnail.d.ts +34 -0
  80. package/dist/components/image-thumbnail.js +58 -0
  81. package/dist/components/image-viewer.d.ts +60 -0
  82. package/dist/components/image-viewer.js +411 -0
  83. package/dist/components/inline-edit.d.ts +22 -0
  84. package/dist/components/inline-edit.js +51 -0
  85. package/dist/components/input.d.ts +155 -0
  86. package/dist/components/input.js +109 -0
  87. package/dist/components/item-label.d.ts +11 -0
  88. package/dist/components/item-label.js +5 -0
  89. package/dist/components/item-row.d.ts +23 -0
  90. package/dist/components/item-row.js +19 -0
  91. package/dist/components/kbd.d.ts +55 -0
  92. package/dist/components/kbd.js +86 -0
  93. package/dist/components/link-policy.d.ts +24 -0
  94. package/dist/components/link-policy.js +32 -0
  95. package/dist/components/linked-text.d.ts +15 -0
  96. package/dist/components/linked-text.js +48 -0
  97. package/dist/components/loading-region.d.ts +18 -0
  98. package/dist/components/loading-region.js +17 -0
  99. package/dist/components/markdown.d.ts +34 -0
  100. package/dist/components/markdown.js +307 -0
  101. package/dist/components/menu.d.ts +132 -0
  102. package/dist/components/menu.js +102 -0
  103. package/dist/components/message-bubble.d.ts +57 -0
  104. package/dist/components/message-bubble.js +28 -0
  105. package/dist/components/meta-line.d.ts +28 -0
  106. package/dist/components/meta-line.js +36 -0
  107. package/dist/components/money-input.d.ts +23 -0
  108. package/dist/components/money-input.js +34 -0
  109. package/dist/components/native-select.d.ts +120 -0
  110. package/dist/components/native-select.js +85 -0
  111. package/dist/components/page-trail.d.ts +30 -0
  112. package/dist/components/page-trail.js +43 -0
  113. package/dist/components/page.d.ts +80 -0
  114. package/dist/components/page.js +60 -0
  115. package/dist/components/path-text.d.ts +8 -0
  116. package/dist/components/path-text.js +11 -0
  117. package/dist/components/percent-input.d.ts +20 -0
  118. package/dist/components/percent-input.js +32 -0
  119. package/dist/components/popover.d.ts +52 -0
  120. package/dist/components/popover.js +36 -0
  121. package/dist/components/progress.d.ts +389 -0
  122. package/dist/components/progress.js +142 -0
  123. package/dist/components/radio.d.ts +65 -0
  124. package/dist/components/radio.js +54 -0
  125. package/dist/components/remote-image-gate.d.ts +34 -0
  126. package/dist/components/remote-image-gate.js +60 -0
  127. package/dist/components/resizable.d.ts +23 -0
  128. package/dist/components/resizable.js +30 -0
  129. package/dist/components/rolling-number.d.ts +13 -0
  130. package/dist/components/rolling-number.js +69 -0
  131. package/dist/components/scroll-area.d.ts +17 -0
  132. package/dist/components/scroll-area.js +25 -0
  133. package/dist/components/section-label.d.ts +10 -0
  134. package/dist/components/section-label.js +21 -0
  135. package/dist/components/section-nav.d.ts +22 -0
  136. package/dist/components/section-nav.js +80 -0
  137. package/dist/components/segmented-control.d.ts +125 -0
  138. package/dist/components/segmented-control.js +101 -0
  139. package/dist/components/select.d.ts +120 -0
  140. package/dist/components/select.js +89 -0
  141. package/dist/components/separator.d.ts +10 -0
  142. package/dist/components/separator.js +12 -0
  143. package/dist/components/sheet.d.ts +101 -0
  144. package/dist/components/sheet.js +129 -0
  145. package/dist/components/shortcut-recorder.d.ts +31 -0
  146. package/dist/components/shortcut-recorder.js +67 -0
  147. package/dist/components/skeleton.d.ts +31 -0
  148. package/dist/components/skeleton.js +33 -0
  149. package/dist/components/slider.d.ts +138 -0
  150. package/dist/components/slider.js +98 -0
  151. package/dist/components/spinner.d.ts +37 -0
  152. package/dist/components/spinner.js +16 -0
  153. package/dist/components/switch.d.ts +59 -0
  154. package/dist/components/switch.js +56 -0
  155. package/dist/components/tab-strip.d.ts +29 -0
  156. package/dist/components/tab-strip.js +42 -0
  157. package/dist/components/tabs.d.ts +13 -0
  158. package/dist/components/tabs.js +68 -0
  159. package/dist/components/text-link.d.ts +54 -0
  160. package/dist/components/text-link.js +38 -0
  161. package/dist/components/textarea.d.ts +75 -0
  162. package/dist/components/textarea.js +90 -0
  163. package/dist/components/toast.d.ts +74 -0
  164. package/dist/components/toast.js +128 -0
  165. package/dist/components/tooltip.d.ts +50 -0
  166. package/dist/components/tooltip.js +46 -0
  167. package/dist/components/truncated-text.d.ts +15 -0
  168. package/dist/components/truncated-text.js +19 -0
  169. package/dist/i18n/defaults.d.ts +7 -0
  170. package/dist/i18n/defaults.js +7 -0
  171. package/dist/i18n/format.d.ts +84 -0
  172. package/dist/i18n/format.js +169 -0
  173. package/dist/i18n/index.d.ts +6 -0
  174. package/dist/i18n/index.js +8 -0
  175. package/dist/i18n/locales/ru-formatters.d.ts +3 -0
  176. package/dist/i18n/locales/ru-formatters.js +22 -0
  177. package/dist/i18n/locales/ru.d.ts +5 -0
  178. package/dist/i18n/locales/ru.js +185 -0
  179. package/dist/i18n/plural.d.ts +20 -0
  180. package/dist/i18n/plural.js +26 -0
  181. package/dist/i18n/provider.d.ts +36 -0
  182. package/dist/i18n/provider.js +39 -0
  183. package/dist/i18n/rich.d.ts +14 -0
  184. package/dist/i18n/rich.js +44 -0
  185. package/dist/i18n/ru-plural.d.ts +3 -0
  186. package/dist/i18n/ru-plural.js +4 -0
  187. package/dist/i18n/strings/alert-dialog.d.ts +5 -0
  188. package/dist/i18n/strings/alert-dialog.js +5 -0
  189. package/dist/i18n/strings/block-stack.d.ts +17 -0
  190. package/dist/i18n/strings/block-stack.js +11 -0
  191. package/dist/i18n/strings/code-block.d.ts +18 -0
  192. package/dist/i18n/strings/code-block.js +13 -0
  193. package/dist/i18n/strings/combobox.d.ts +12 -0
  194. package/dist/i18n/strings/combobox.js +10 -0
  195. package/dist/i18n/strings/command-palette.d.ts +17 -0
  196. package/dist/i18n/strings/command-palette.js +11 -0
  197. package/dist/i18n/strings/diff-view.d.ts +92 -0
  198. package/dist/i18n/strings/diff-view.js +68 -0
  199. package/dist/i18n/strings/image-viewer.d.ts +43 -0
  200. package/dist/i18n/strings/image-viewer.js +32 -0
  201. package/dist/i18n/strings/index.d.ts +314 -0
  202. package/dist/i18n/strings/index.js +43 -0
  203. package/dist/i18n/strings/kbd.d.ts +42 -0
  204. package/dist/i18n/strings/kbd.js +39 -0
  205. package/dist/i18n/strings/markdown.d.ts +23 -0
  206. package/dist/i18n/strings/markdown.js +18 -0
  207. package/dist/i18n/strings/money.d.ts +7 -0
  208. package/dist/i18n/strings/money.js +5 -0
  209. package/dist/i18n/strings/primitives.d.ts +25 -0
  210. package/dist/i18n/strings/primitives.js +18 -0
  211. package/dist/i18n/strings/progress.d.ts +4 -0
  212. package/dist/i18n/strings/progress.js +4 -0
  213. package/dist/i18n/strings/shortcut-recorder.d.ts +15 -0
  214. package/dist/i18n/strings/shortcut-recorder.js +11 -0
  215. package/dist/i18n/strings/time.d.ts +41 -0
  216. package/dist/i18n/strings/time.js +20 -0
  217. package/dist/i18n/strings/toast.d.ts +6 -0
  218. package/dist/i18n/strings/toast.js +6 -0
  219. package/dist/i18n/time-zones.d.ts +20 -0
  220. package/dist/i18n/time-zones.js +64 -0
  221. package/dist/index.d.ts +84 -0
  222. package/dist/index.js +88 -0
  223. package/dist/lib/ansi.d.ts +55 -0
  224. package/dist/lib/ansi.js +438 -0
  225. package/dist/lib/clock.d.ts +32 -0
  226. package/dist/lib/clock.js +106 -0
  227. package/dist/lib/cn.d.ts +12 -0
  228. package/dist/lib/cn.js +28 -0
  229. package/dist/lib/control-props.d.ts +21 -0
  230. package/dist/lib/control-props.js +14 -0
  231. package/dist/lib/direction.d.ts +13 -0
  232. package/dist/lib/direction.js +25 -0
  233. package/dist/lib/image-size.d.ts +12 -0
  234. package/dist/lib/image-size.js +66 -0
  235. package/dist/lib/keycaps.d.ts +21 -0
  236. package/dist/lib/keycaps.js +62 -0
  237. package/dist/lib/keyed.d.ts +9 -0
  238. package/dist/lib/keyed.js +14 -0
  239. package/dist/lib/keys.d.ts +49 -0
  240. package/dist/lib/keys.js +159 -0
  241. package/dist/lib/kit-motion.d.ts +9 -0
  242. package/dist/lib/kit-motion.js +12 -0
  243. package/dist/lib/link-check-dialog.d.ts +11 -0
  244. package/dist/lib/link-check-dialog.js +18 -0
  245. package/dist/lib/motion-features.d.ts +1 -0
  246. package/dist/lib/motion-features.js +4 -0
  247. package/dist/lib/motion-provider.d.ts +5 -0
  248. package/dist/lib/motion-provider.js +8 -0
  249. package/dist/lib/motion.d.ts +39 -0
  250. package/dist/lib/motion.js +49 -0
  251. package/dist/lib/number-format.d.ts +114 -0
  252. package/dist/lib/number-format.js +313 -0
  253. package/dist/lib/patch.d.ts +72 -0
  254. package/dist/lib/patch.js +344 -0
  255. package/dist/lib/path.d.ts +34 -0
  256. package/dist/lib/path.js +85 -0
  257. package/dist/lib/search.d.ts +10 -0
  258. package/dist/lib/search.js +20 -0
  259. package/dist/lib/shiki-themes.d.ts +7 -0
  260. package/dist/lib/shiki-themes.js +115 -0
  261. package/dist/lib/shiki.d.ts +101 -0
  262. package/dist/lib/shiki.js +353 -0
  263. package/dist/lib/shortcut-context.d.ts +23 -0
  264. package/dist/lib/shortcut-context.js +29 -0
  265. package/dist/lib/text.d.ts +56 -0
  266. package/dist/lib/text.js +149 -0
  267. package/dist/lib/theme-scope.d.ts +18 -0
  268. package/dist/lib/theme-scope.js +24 -0
  269. package/dist/lib/theme.d.ts +40 -0
  270. package/dist/lib/theme.js +96 -0
  271. package/dist/lib/tree-keys.d.ts +72 -0
  272. package/dist/lib/tree-keys.js +126 -0
  273. package/dist/lib/url.d.ts +17 -0
  274. package/dist/lib/url.js +38 -0
  275. package/dist/lib/use-copy-to-clipboard.d.ts +13 -0
  276. package/dist/lib/use-copy-to-clipboard.js +40 -0
  277. package/dist/lib/use-delayed-loading.d.ts +11 -0
  278. package/dist/lib/use-delayed-loading.js +31 -0
  279. package/dist/lib/use-file-picker.d.ts +26 -0
  280. package/dist/lib/use-file-picker.js +43 -0
  281. package/dist/lib/use-hydrated.d.ts +6 -0
  282. package/dist/lib/use-hydrated.js +11 -0
  283. package/dist/lib/use-latest.d.ts +12 -0
  284. package/dist/lib/use-latest.js +22 -0
  285. package/dist/lib/use-resolved-theme.d.ts +12 -0
  286. package/dist/lib/use-resolved-theme.js +25 -0
  287. package/dist/styles/base.css +393 -0
  288. package/dist/styles/code.css +35 -0
  289. package/dist/styles/fonts.css +12 -0
  290. package/dist/styles/index.css +13 -0
  291. package/dist/styles/tokens.gen.css +494 -0
  292. package/package.json +208 -4
@@ -0,0 +1,25 @@
1
+ "use client";
2
+ import { useDirection } from "@base-ui/react/direction-provider";
3
+ /**
4
+ * +1 left to right, -1 right to left: multiply a horizontal motion offset by it so things slide in
5
+ * from the side they would in the person's language (a step forward comes from the end edge). The
6
+ * direction is the one `UiI18nProvider` gives Base UI.
7
+ */
8
+ export function useInlineSign() {
9
+ return useDirection() === "rtl" ? -1 : 1;
10
+ }
11
+ /**
12
+ * A horizontal arrow key as a left-to-right reader means it, given `useInlineSign()`'s sign: right to
13
+ * left, ArrowRight points toward the start of the line and ArrowLeft toward its end (WAI-ARIA APG), so
14
+ * they swap. A handler written for left to right (ArrowRight opens a tree node, moves to the next
15
+ * column) then follows the reading direction. Other keys pass through.
16
+ */
17
+ export function logicalArrow(key, sign) {
18
+ if (sign === 1)
19
+ return key;
20
+ if (key === "ArrowLeft")
21
+ return "ArrowRight";
22
+ if (key === "ArrowRight")
23
+ return "ArrowLeft";
24
+ return key;
25
+ }
@@ -0,0 +1,12 @@
1
+ /** An image's own size in pixels, as its file header gives it. */
2
+ export interface ImageSize {
3
+ readonly width: number;
4
+ readonly height: number;
5
+ }
6
+ /**
7
+ * The size a PNG, JPEG, GIF or WebP declares in its header, from its first bytes (a JPEG's frame
8
+ * header can come after its metadata: give it the first 64 KB); undefined for anything else.
9
+ */
10
+ export declare function imageSize(bytes: Uint8Array): ImageSize | undefined;
11
+ /** The size an inline (base64) image declares, decoding only its first 64 KB. */
12
+ export declare function base64ImageSize(base64: string): ImageSize | undefined;
@@ -0,0 +1,66 @@
1
+ const ascii = (bytes, at, length) => String.fromCharCode(...bytes.subarray(at, at + length));
2
+ const u16be = (bytes, at) => ((bytes[at] ?? 0) << 8) | (bytes[at + 1] ?? 0);
3
+ const u16le = (bytes, at) => (bytes[at] ?? 0) | ((bytes[at + 1] ?? 0) << 8);
4
+ const u24le = (bytes, at) => u16le(bytes, at) | ((bytes[at + 2] ?? 0) << 16);
5
+ const u32be = (bytes, at) => u16be(bytes, at) * 0x1_00_00 + u16be(bytes, at + 2);
6
+ const sized = (width, height) => width > 0 && height > 0 ? { width, height } : undefined;
7
+ /** A JPEG's size from its first start-of-frame marker (SOF0–SOF15, not DHT, JPG or DAC). */
8
+ function jpegSize(bytes) {
9
+ let at = 2;
10
+ while (at + 9 < bytes.length) {
11
+ if (bytes[at] !== 0xff)
12
+ return undefined;
13
+ const marker = bytes[at + 1] ?? 0;
14
+ if (marker === 0xff) {
15
+ at += 1;
16
+ continue;
17
+ }
18
+ const frame = marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc;
19
+ if (frame)
20
+ return sized(u16be(bytes, at + 7), u16be(bytes, at + 5));
21
+ at += 2 + u16be(bytes, at + 2);
22
+ }
23
+ return undefined;
24
+ }
25
+ function webpSize(bytes) {
26
+ const chunk = ascii(bytes, 12, 4);
27
+ if (chunk === "VP8X")
28
+ return sized(u24le(bytes, 24) + 1, u24le(bytes, 27) + 1);
29
+ if (chunk === "VP8L") {
30
+ const b = (i) => bytes[21 + i] ?? 0;
31
+ return sized(1 + (((b(1) & 0x3f) << 8) | b(0)), 1 + (((b(3) & 0x0f) << 10) | (b(2) << 2) | ((b(1) & 0xc0) >> 6)));
32
+ }
33
+ if (chunk === "VP8 ")
34
+ return sized(u16le(bytes, 26) & 0x3fff, u16le(bytes, 28) & 0x3fff);
35
+ return undefined;
36
+ }
37
+ /**
38
+ * The size a PNG, JPEG, GIF or WebP declares in its header, from its first bytes (a JPEG's frame
39
+ * header can come after its metadata: give it the first 64 KB); undefined for anything else.
40
+ */
41
+ export function imageSize(bytes) {
42
+ if (bytes[0] === 0x89 && ascii(bytes, 1, 3) === "PNG")
43
+ return sized(u32be(bytes, 16), u32be(bytes, 20));
44
+ if (ascii(bytes, 0, 4) === "GIF8")
45
+ return sized(u16le(bytes, 6), u16le(bytes, 8));
46
+ if (ascii(bytes, 0, 4) === "RIFF" && ascii(bytes, 8, 4) === "WEBP")
47
+ return webpSize(bytes);
48
+ if (bytes[0] === 0xff && bytes[1] === 0xd8)
49
+ return jpegSize(bytes);
50
+ return undefined;
51
+ }
52
+ /** How much of a base64 image to read for its size: 64 KB of bytes. */
53
+ const HEADER_CHARS = 87_384;
54
+ /** The size an inline (base64) image declares, decoding only its first 64 KB. */
55
+ export function base64ImageSize(base64) {
56
+ try {
57
+ const binary = atob(base64.slice(0, HEADER_CHARS));
58
+ const bytes = new Uint8Array(binary.length);
59
+ for (let i = 0; i < binary.length; i += 1)
60
+ bytes[i] = binary.charCodeAt(i);
61
+ return imageSize(bytes);
62
+ }
63
+ catch {
64
+ return undefined;
65
+ }
66
+ }
@@ -0,0 +1,21 @@
1
+ import { type UiStrings } from "../i18n/strings/index.js";
2
+ /**
3
+ * Keys as text, with no React and no DOM, for the kit's keycaps and for hosts that print text (an
4
+ * approval card in the CLI): a logical key's glyph, a chord as plain text, and the keycaps of a
5
+ * desktop accelerator. The caller says whether keys read as on a Mac (`isMacPlatform()` in a page,
6
+ * the host's own platform elsewhere).
7
+ */
8
+ type KbdStrings = UiStrings["kbd"];
9
+ /**
10
+ * Maps logical key names to platform glyphs: `mod` → ⌘ on Apple, Ctrl elsewhere. Key words come
11
+ * from `strings` (`useUiStrings().kbd`; English by default).
12
+ */
13
+ export declare function keyGlyph(key: string, mac: boolean, strings?: KbdStrings): string;
14
+ /** A chord as plain text, for placeholders and sentences: "⌘↵" on Apple, "Ctrl+Enter" elsewhere. */
15
+ export declare function shortcutText(keys: readonly string[], mac: boolean, strings?: KbdStrings): string;
16
+ /**
17
+ * The keycaps a desktop accelerator shows, as logical keys: the summon shortcut as the desktop shell
18
+ * takes it, a Tauri accelerator such as "CommandOrControl+Shift+Space" → `["mod", "shift", "space"]`.
19
+ */
20
+ export declare function acceleratorKeys(accelerator: string): string[];
21
+ export {};
@@ -0,0 +1,62 @@
1
+ import { EN_UI_STRINGS } from "../i18n/strings/index.js";
2
+ /**
3
+ * Maps logical key names to platform glyphs: `mod` → ⌘ on Apple, Ctrl elsewhere. Key words come
4
+ * from `strings` (`useUiStrings().kbd`; English by default).
5
+ */
6
+ export function keyGlyph(key, mac, strings = EN_UI_STRINGS.kbd) {
7
+ const k = key.toLowerCase();
8
+ const { keys } = strings;
9
+ const map = mac
10
+ ? { mod: "⌘", cmd: "⌘", ctrl: "⌃", alt: "⌥", option: "⌥", shift: "⇧", enter: "↵", return: "↵" }
11
+ : {
12
+ mod: keys.ctrl,
13
+ cmd: keys.ctrl,
14
+ ctrl: keys.ctrl,
15
+ alt: keys.alt,
16
+ option: keys.alt,
17
+ shift: keys.shift,
18
+ enter: keys.enter,
19
+ return: keys.enter,
20
+ };
21
+ const shared = {
22
+ up: "↑",
23
+ down: "↓",
24
+ left: "←",
25
+ right: "→",
26
+ esc: keys.esc,
27
+ escape: keys.esc,
28
+ tab: keys.tab,
29
+ space: keys.space,
30
+ backspace: "⌫",
31
+ delete: "⌦",
32
+ };
33
+ return map[k] ?? shared[k] ?? (key.length === 1 ? key.toUpperCase() : key);
34
+ }
35
+ /** A chord as plain text, for placeholders and sentences: "⌘↵" on Apple, "Ctrl+Enter" elsewhere. */
36
+ export function shortcutText(keys, mac, strings = EN_UI_STRINGS.kbd) {
37
+ return keys.map((key) => keyGlyph(key, mac, strings)).join(mac ? "" : "+");
38
+ }
39
+ /**
40
+ * An accelerator's modifier and key names as logical keys, and the keys its name doesn't show
41
+ * ("Backquote" is the key marked `); the rest are the key as written ("K", "F5", "Up").
42
+ */
43
+ const KEYCAP = {
44
+ CommandOrControl: "mod",
45
+ CmdOrCtrl: "mod",
46
+ Command: "cmd",
47
+ Control: "ctrl",
48
+ Ctrl: "ctrl",
49
+ Alt: "alt",
50
+ Option: "alt",
51
+ Shift: "shift",
52
+ Space: "space",
53
+ Enter: "enter",
54
+ Backquote: "`",
55
+ };
56
+ /**
57
+ * The keycaps a desktop accelerator shows, as logical keys: the summon shortcut as the desktop shell
58
+ * takes it, a Tauri accelerator such as "CommandOrControl+Shift+Space" → `["mod", "shift", "space"]`.
59
+ */
60
+ export function acceleratorKeys(accelerator) {
61
+ return accelerator.split("+").map((part) => KEYCAP[part] ?? part);
62
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Stable React keys for a list of values: each value's own key, with `#n` telling repeats apart. For
3
+ * pieces with no id (a message's images, a path's segments), so a list never leans on the map index.
4
+ * `@nibapp/ui/keyed`, a preview API: it may change in a minor version before 1.0.
5
+ */
6
+ export declare function keyed<T>(items: readonly T[], keyOf: (item: T) => string): {
7
+ readonly item: T;
8
+ readonly key: string;
9
+ }[];
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Stable React keys for a list of values: each value's own key, with `#n` telling repeats apart. For
3
+ * pieces with no id (a message's images, a path's segments), so a list never leans on the map index.
4
+ * `@nibapp/ui/keyed`, a preview API: it may change in a minor version before 1.0.
5
+ */
6
+ export function keyed(items, keyOf) {
7
+ const seen = new Map();
8
+ return items.map((item) => {
9
+ const base = keyOf(item);
10
+ const count = seen.get(base) ?? 0;
11
+ seen.set(base, count + 1);
12
+ return { item, key: `${base}#${count}` };
13
+ });
14
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Keys, the kit's way: chords as logical keys (`["mod", "k"]`: `mod` is ⌘ on Apple platforms, Ctrl
3
+ * elsewhere, see `keyGlyph` in keycaps.ts), how a key event matches one on any keyboard layout, the
4
+ * `aria-keyshortcuts` a control announces, and the input-method guard every Enter handler uses. No
5
+ * table of bindings: a host's keymap reaches the kit's tooltips and menus through `ShortcutProvider`.
6
+ */
7
+ /** Whether a key event lands where text goes (a field, a select, editable content): single-key shortcuts stand down there. */
8
+ export declare function isTypingTarget(target: EventTarget | null): boolean;
9
+ type ComposingKey = Pick<KeyboardEvent, "isComposing" | "keyCode">;
10
+ /**
11
+ * A key pressed while an input method composes text: Chinese, Japanese, Korean and other input
12
+ * confirm a candidate with Enter, so a handler that acts on Enter skips it. WebKit, the desktop app's
13
+ * engine, sends the confirming Enter after `compositionend` with `isComposing` false and `keyCode`
14
+ * 229, so both count. Takes a DOM or a React keyboard event.
15
+ */
16
+ export declare function isComposing(event: ComposingKey | {
17
+ readonly nativeEvent: ComposingKey;
18
+ }): boolean;
19
+ /** macOS, iPhone or iPad: `mod` is ⌘. */
20
+ export declare function isMacPlatform(): boolean;
21
+ /** Windows. */
22
+ export declare function isWindowsPlatform(): boolean;
23
+ /**
24
+ * The keys a key event stands for in a shortcut, lower-cased: a letter, or an unshifted digit, by its
25
+ * position (`code`), so a shortcut works on any layout (the key marked Y types "н" on a Russian one);
26
+ * punctuation and named keys as typed ("?", "@", "enter") and by position ("[" for ⌘⇧[, which types
27
+ * "{", or where a Russian layout types "х"). Every matcher, the kit's and its host's, reads these.
28
+ */
29
+ export declare function shortcutKeys(event: Pick<KeyboardEvent, "key" | "shiftKey"> & {
30
+ readonly code?: string | undefined;
31
+ }): string[];
32
+ /**
33
+ * Whether a keyboard event matches a chord's keys (exact modifiers; `mod` resolves per platform).
34
+ * Single-key chords like `j` / `k` only match without modifiers.
35
+ */
36
+ export declare function matchesShortcut(event: Pick<KeyboardEvent, "key" | "metaKey" | "ctrlKey" | "shiftKey" | "altKey"> & {
37
+ readonly code?: string | undefined;
38
+ }, keys: readonly string[], mac?: boolean): boolean;
39
+ /**
40
+ * The `aria-keyshortcuts` value for a chord (`["mod", "n"]` → "Meta+N" on Apple, "Control+N"
41
+ * elsewhere). Put it on the control the shortcut triggers; the visible `<Shortcut>` inside it is
42
+ * `aria-hidden`, so the control's name stays "New document" instead of "New document⌘N".
43
+ */
44
+ export declare function ariaKeyShortcuts(keys: readonly string[], mac?: boolean): string;
45
+ /** Spread onto a control: `{ "aria-keyshortcuts": "Meta+N" }`, or nothing when there is no chord. */
46
+ export declare function keyShortcutsProps(keys: readonly string[] | undefined, mac?: boolean): {
47
+ "aria-keyshortcuts"?: string;
48
+ };
49
+ export {};
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Keys, the kit's way: chords as logical keys (`["mod", "k"]`: `mod` is ⌘ on Apple platforms, Ctrl
3
+ * elsewhere, see `keyGlyph` in keycaps.ts), how a key event matches one on any keyboard layout, the
4
+ * `aria-keyshortcuts` a control announces, and the input-method guard every Enter handler uses. No
5
+ * table of bindings: a host's keymap reaches the kit's tooltips and menus through `ShortcutProvider`.
6
+ */
7
+ function isRecord(value) {
8
+ return typeof value === "object" && value !== null;
9
+ }
10
+ /** Whether a key event lands where text goes (a field, a select, editable content): single-key shortcuts stand down there. */
11
+ export function isTypingTarget(target) {
12
+ return (target instanceof HTMLElement &&
13
+ (target.isContentEditable || target.closest("input, textarea, select") !== null));
14
+ }
15
+ /**
16
+ * A key pressed while an input method composes text: Chinese, Japanese, Korean and other input
17
+ * confirm a candidate with Enter, so a handler that acts on Enter skips it. WebKit, the desktop app's
18
+ * engine, sends the confirming Enter after `compositionend` with `isComposing` false and `keyCode`
19
+ * 229, so both count. Takes a DOM or a React keyboard event.
20
+ */
21
+ export function isComposing(event) {
22
+ const key = "nativeEvent" in event ? event.nativeEvent : event;
23
+ return key.isComposing || key.keyCode === 229;
24
+ }
25
+ /**
26
+ * The page's platform name: the User-Agent client hint where the browser has one (TypeScript's DOM
27
+ * types don't know it yet), else `navigator.platform`; empty where there is no browser.
28
+ */
29
+ function platformName() {
30
+ if (typeof navigator === "undefined")
31
+ return "";
32
+ const hints = "userAgentData" in navigator ? navigator.userAgentData : undefined;
33
+ const hinted = isRecord(hints) ? hints["platform"] : undefined;
34
+ return typeof hinted === "string" ? hinted : (navigator.platform ?? "");
35
+ }
36
+ /** macOS, iPhone or iPad: `mod` is ⌘. */
37
+ export function isMacPlatform() {
38
+ return /mac|iphone|ipad/iu.test(platformName());
39
+ }
40
+ /** Windows. */
41
+ export function isWindowsPlatform() {
42
+ return /^win/iu.test(platformName());
43
+ }
44
+ const KEY_ALIASES = {
45
+ esc: "escape",
46
+ return: "enter",
47
+ up: "arrowup",
48
+ down: "arrowdown",
49
+ left: "arrowleft",
50
+ right: "arrowright",
51
+ space: " ",
52
+ };
53
+ /** Punctuation keys by position, as a US layout types them. */
54
+ const PUNCTUATION_CODES = {
55
+ Backquote: "`",
56
+ Backslash: "\\",
57
+ BracketLeft: "[",
58
+ BracketRight: "]",
59
+ Comma: ",",
60
+ Period: ".",
61
+ Slash: "/",
62
+ Semicolon: ";",
63
+ Quote: "'",
64
+ Minus: "-",
65
+ Equal: "=",
66
+ };
67
+ /**
68
+ * The keys a key event stands for in a shortcut, lower-cased: a letter, or an unshifted digit, by its
69
+ * position (`code`), so a shortcut works on any layout (the key marked Y types "н" on a Russian one);
70
+ * punctuation and named keys as typed ("?", "@", "enter") and by position ("[" for ⌘⇧[, which types
71
+ * "{", or where a Russian layout types "х"). Every matcher, the kit's and its host's, reads these.
72
+ */
73
+ export function shortcutKeys(event) {
74
+ const code = event.code ?? "";
75
+ const letter = /^Key([A-Z])$/u.exec(code)?.[1];
76
+ if (letter !== undefined)
77
+ return [letter.toLowerCase()];
78
+ const digit = /^Digit(\d)$/u.exec(code)?.[1];
79
+ if (digit !== undefined && !event.shiftKey)
80
+ return [digit];
81
+ const typed = event.key.toLowerCase();
82
+ const placed = PUNCTUATION_CODES[code];
83
+ return placed === undefined || placed === typed ? [typed] : [typed, placed];
84
+ }
85
+ /**
86
+ * Whether a keyboard event matches a chord's keys (exact modifiers; `mod` resolves per platform).
87
+ * Single-key chords like `j` / `k` only match without modifiers.
88
+ */
89
+ export function matchesShortcut(event, keys, mac = isMacPlatform()) {
90
+ const want = { meta: false, ctrl: false, shift: false, alt: false };
91
+ let main;
92
+ for (const raw of keys) {
93
+ const k = raw.toLowerCase();
94
+ if (k === "mod")
95
+ want[mac ? "meta" : "ctrl"] = true;
96
+ else if (k === "cmd" || k === "meta")
97
+ want.meta = true;
98
+ else if (k === "ctrl")
99
+ want.ctrl = true;
100
+ else if (k === "shift")
101
+ want.shift = true;
102
+ else if (k === "alt" || k === "option")
103
+ want.alt = true;
104
+ else
105
+ main = KEY_ALIASES[k] ?? k;
106
+ }
107
+ if (main === undefined)
108
+ return false;
109
+ // Shifted punctuation ("@") arrives already shifted; don't require the flag for it.
110
+ const shiftOk = want.shift === event.shiftKey || (main.length === 1 && !/[a-z0-9]/.test(main));
111
+ return (shortcutKeys(event).includes(main) &&
112
+ event.metaKey === want.meta &&
113
+ event.ctrlKey === want.ctrl &&
114
+ event.altKey === want.alt &&
115
+ shiftOk);
116
+ }
117
+ const ARIA_KEYS = {
118
+ cmd: "Meta",
119
+ meta: "Meta",
120
+ ctrl: "Control",
121
+ // i18n-ignore: an aria-keyshortcuts key name (UI Events `key` value), the same in every locale
122
+ alt: "Alt",
123
+ option: "Alt",
124
+ shift: "Shift",
125
+ enter: "Enter",
126
+ return: "Enter",
127
+ esc: "Escape",
128
+ escape: "Escape",
129
+ tab: "Tab",
130
+ space: "Space",
131
+ backspace: "Backspace",
132
+ delete: "Delete",
133
+ up: "ArrowUp",
134
+ down: "ArrowDown",
135
+ left: "ArrowLeft",
136
+ right: "ArrowRight",
137
+ "+": "Plus",
138
+ };
139
+ /**
140
+ * The `aria-keyshortcuts` value for a chord (`["mod", "n"]` → "Meta+N" on Apple, "Control+N"
141
+ * elsewhere). Put it on the control the shortcut triggers; the visible `<Shortcut>` inside it is
142
+ * `aria-hidden`, so the control's name stays "New document" instead of "New document⌘N".
143
+ */
144
+ export function ariaKeyShortcuts(keys, mac = isMacPlatform()) {
145
+ return keys
146
+ .map((raw) => {
147
+ const k = raw.toLowerCase();
148
+ if (k === "mod")
149
+ return mac ? "Meta" : "Control";
150
+ return ARIA_KEYS[k] ?? (raw.length === 1 ? raw.toUpperCase() : raw);
151
+ })
152
+ .join("+");
153
+ }
154
+ /** Spread onto a control: `{ "aria-keyshortcuts": "Meta+N" }`, or nothing when there is no chord. */
155
+ export function keyShortcutsProps(keys, mac = isMacPlatform()) {
156
+ if (keys === undefined || keys.length === 0)
157
+ return {};
158
+ return { "aria-keyshortcuts": ariaKeyShortcuts(keys, mac) };
159
+ }
@@ -0,0 +1,9 @@
1
+ import type { ReactNode } from "react";
2
+ /**
3
+ * Where a kit component's `motion/react-m` elements get their animations. The features load the first time one
4
+ * renders, so a page that never animates doesn't carry them, and every animated component wraps itself, so it animates
5
+ * with no provider from the app. An element's first animation waits for them (a few milliseconds, once).
6
+ */
7
+ export declare function KitMotion({ children }: {
8
+ readonly children: ReactNode;
9
+ }): import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { LazyMotion } from "motion/react";
4
+ const features = () => import("./motion-features.js").then((module) => module.domAnimation);
5
+ /**
6
+ * Where a kit component's `motion/react-m` elements get their animations. The features load the first time one
7
+ * renders, so a page that never animates doesn't carry them, and every animated component wraps itself, so it animates
8
+ * with no provider from the app. An element's first animation waits for them (a few milliseconds, once).
9
+ */
10
+ export function KitMotion({ children }) {
11
+ return _jsx(LazyMotion, { features: features, children: children });
12
+ }
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from "react";
2
+ /**
3
+ * `ExternalLinkCheck`'s dialog (components/link-policy.tsx), loaded only where a check renders: the whole address,
4
+ * then Open (as the link would have, in a new tab with no opener), Copy or Cancel. `asked` opens it at once, for a
5
+ * click on the link while this was still loading.
6
+ */
7
+ export declare function LinkCheckDialog({ href, trigger, asked, }: {
8
+ readonly href: string;
9
+ readonly trigger: (open: () => void) => ReactNode;
10
+ readonly asked: boolean;
11
+ }): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { Button } from "../components/button.js";
5
+ import { Dialog, DialogClose } from "../components/dialog.js";
6
+ import { useUiStrings } from "../i18n/provider.js";
7
+ import { useCopyToClipboard } from "./use-copy-to-clipboard.js";
8
+ /**
9
+ * `ExternalLinkCheck`'s dialog (components/link-policy.tsx), loaded only where a check renders: the whole address,
10
+ * then Open (as the link would have, in a new tab with no opener), Copy or Cancel. `asked` opens it at once, for a
11
+ * click on the link while this was still loading.
12
+ */
13
+ export function LinkCheckDialog({ href, trigger, asked, }) {
14
+ const t = useUiStrings().markdown.linkCheck;
15
+ const [open, setOpen] = useState(asked);
16
+ const [copied, copy] = useCopyToClipboard();
17
+ return (_jsxs(_Fragment, { children: [trigger(() => setOpen(true)), _jsx(Dialog, { open: open, onOpenChange: setOpen, title: t.title, description: t.description, footer: _jsxs(_Fragment, { children: [_jsx(DialogClose, { render: _jsx(Button, { variant: "ghost" }), children: t.cancel }), _jsx(Button, { variant: "secondary", onClick: () => copy(href), children: copied ? t.copied : t.copy }), _jsx(Button, { nativeButton: false, render: _jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer" }), onClick: () => setOpen(false), children: t.open })] }), children: _jsx("p", { "data-slot": "link-check-address", className: "break-all font-mono text-sm text-fg", children: href }) })] }));
18
+ }
@@ -0,0 +1 @@
1
+ export { domAnimation } from "motion/react";
@@ -0,0 +1,4 @@
1
+ // The animation features the kit's `motion/react-m` elements use (animate, exit, variants, the gestures): loaded by
2
+ // `KitMotion` once a kit animation first renders, apart from the page's first bundle. No kit module uses layout
3
+ // animation or drag, which `domMax` would add.
4
+ export { domAnimation } from "motion/react";
@@ -0,0 +1,5 @@
1
+ import type { ReactNode } from "react";
2
+ /** App-wide motion defaults: a 180 ms tween, and honor the OS reduced-motion setting. */
3
+ export declare function MotionProvider({ children }: {
4
+ children: ReactNode;
5
+ }): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { MotionConfig } from "motion/react";
4
+ import { tween } from "./motion.js";
5
+ /** App-wide motion defaults: a 180 ms tween, and honor the OS reduced-motion setting. */
6
+ export function MotionProvider({ children }) {
7
+ return (_jsx(MotionConfig, { reducedMotion: "user", transition: tween.base, children: children }));
8
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Motion presets for `motion/react`. Keep motion purposeful: functional UI settles within 200 ms,
3
+ * no blur and no gratuitous bounce outside micro-moments. Every preset degrades gracefully under
4
+ * `prefers-reduced-motion` when the tree is wrapped in <MotionProvider> (reducedMotion="user").
5
+ */
6
+ import { EASINGS, type SpringName } from "../_bundled/tokens/index.js";
7
+ import type { BezierDefinition, Transition, Variants } from "motion/react";
8
+ export declare const spring: Record<SpringName, Transition>;
9
+ /** Durations in seconds (Motion's unit). */
10
+ export declare const duration: {
11
+ readonly instant: number;
12
+ readonly fast: number;
13
+ readonly base: number;
14
+ readonly moderate: number;
15
+ readonly slow: number;
16
+ readonly slower: number;
17
+ readonly glacial: number;
18
+ };
19
+ export declare const ease: Record<keyof typeof EASINGS, BezierDefinition>;
20
+ /** Tween helpers for opacity-only or color transitions where springs add nothing. */
21
+ export declare const tween: {
22
+ fast: {
23
+ duration: number;
24
+ ease: BezierDefinition;
25
+ };
26
+ base: {
27
+ duration: number;
28
+ ease: BezierDefinition;
29
+ };
30
+ exit: {
31
+ duration: number;
32
+ ease: BezierDefinition;
33
+ };
34
+ };
35
+ export declare const fade: Variants;
36
+ export declare const rise: Variants;
37
+ export declare const pop: Variants;
38
+ /** Staggered list entrance: put on the parent, use `rise` on children. */
39
+ export declare const stagger: (each?: number, delayChildren?: number) => Variants;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Motion presets for `motion/react`. Keep motion purposeful: functional UI settles within 200 ms,
3
+ * no blur and no gratuitous bounce outside micro-moments. Every preset degrades gracefully under
4
+ * `prefers-reduced-motion` when the tree is wrapped in <MotionProvider> (reducedMotion="user").
5
+ */
6
+ import { DURATIONS, EASINGS, SPRINGS } from "../_bundled/tokens/index.js";
7
+ export const spring = {
8
+ snappy: { type: "spring", ...SPRINGS.snappy },
9
+ smooth: { type: "spring", ...SPRINGS.smooth },
10
+ gentle: { type: "spring", ...SPRINGS.gentle },
11
+ bouncy: { type: "spring", ...SPRINGS.bouncy },
12
+ signature: { type: "spring", ...SPRINGS.signature },
13
+ };
14
+ /** Durations in seconds (Motion's unit). */
15
+ export const duration = {
16
+ instant: DURATIONS.instant / 1000,
17
+ fast: DURATIONS.fast / 1000,
18
+ base: DURATIONS.base / 1000,
19
+ moderate: DURATIONS.moderate / 1000,
20
+ slow: DURATIONS.slow / 1000,
21
+ slower: DURATIONS.slower / 1000,
22
+ glacial: DURATIONS.glacial / 1000,
23
+ };
24
+ export const ease = EASINGS;
25
+ /** Tween helpers for opacity-only or color transitions where springs add nothing. */
26
+ export const tween = {
27
+ fast: { duration: duration.fast, ease: ease.standard },
28
+ base: { duration: duration.base, ease: ease.standard },
29
+ exit: { duration: duration.fast, ease: ease.out },
30
+ };
31
+ export const fade = {
32
+ initial: { opacity: 0 },
33
+ animate: { opacity: 1, transition: tween.base },
34
+ exit: { opacity: 0, transition: tween.exit },
35
+ };
36
+ export const rise = {
37
+ initial: { opacity: 0, y: 6 },
38
+ animate: { opacity: 1, y: 0, transition: spring.snappy },
39
+ exit: { opacity: 0, y: -4, transition: tween.exit },
40
+ };
41
+ export const pop = {
42
+ initial: { opacity: 0, scale: 0.94 },
43
+ animate: { opacity: 1, scale: 1, transition: spring.snappy },
44
+ exit: { opacity: 0, scale: 0.97, transition: tween.exit },
45
+ };
46
+ /** Staggered list entrance: put on the parent, use `rise` on children. */
47
+ export const stagger = (each = 0.03, delayChildren = 0) => ({
48
+ animate: { transition: { staggerChildren: each, delayChildren } },
49
+ });