@vvedantb/ui 0.1.5 → 0.2.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 (45) hide show
  1. package/README.md +24 -0
  2. package/app/src/auth/useStableAuth.ts +69 -0
  3. package/app/src/cn.ts +6 -0
  4. package/app/src/confirm/ConfirmSkipHint.tsx +35 -0
  5. package/app/src/confirm/confirmSkip.ts +26 -0
  6. package/app/src/confirm/useAltHeld.ts +128 -0
  7. package/app/src/confirm/useRequestConfirm.ts +25 -0
  8. package/app/src/index.ts +43 -0
  9. package/app/src/settings/SettingsEmptyState.tsx +38 -0
  10. package/app/src/settings/SettingsField.tsx +42 -0
  11. package/app/src/settings/SettingsPage.tsx +88 -0
  12. package/app/src/settings/SettingsSection.tsx +92 -0
  13. package/app/src/settings/SettingsStack.tsx +17 -0
  14. package/app/src/settings/SettingsToggleRow.tsx +51 -0
  15. package/app/src/update/deployFingerprint.ts +27 -0
  16. package/app/src/update/updateWatcher.ts +100 -0
  17. package/package.json +65 -9
  18. package/shell/src/PillTabs.tsx +86 -0
  19. package/shell/src/SidebarHeader.tsx +21 -0
  20. package/shell/src/cn.ts +5 -0
  21. package/shell/src/index.ts +6 -0
  22. package/src/ai-elements/conversation.tsx +1 -1
  23. package/src/ai-elements/suggestion.tsx +1 -1
  24. package/src/index.ts +18 -18
  25. package/src/ui/{_menu-classes.vibot.ts → _menu-classes.alt.ts} +2 -2
  26. package/src/ui/_menu-classes.ts +2 -2
  27. package/tokens/examples/migration.md +303 -0
  28. package/tokens/src/css/animations.css +55 -0
  29. package/tokens/src/css/base.css +51 -0
  30. package/tokens/src/css/dialog.css +13 -0
  31. package/tokens/src/css/index.css +19 -0
  32. package/tokens/src/css/modal.css +44 -0
  33. package/tokens/src/css/scrollbar-thin.css +40 -0
  34. package/tokens/src/css/tabs-pills.css +58 -0
  35. package/tokens/src/css/tabs.css +57 -0
  36. package/tokens/src/css/utilities.css +34 -0
  37. package/tokens/src/native.ts +39 -0
  38. package/tokens/src/tailwind-preset.d.mts +25 -0
  39. package/tokens/src/tailwind-preset.mjs +177 -0
  40. package/tokens/src/tokens.css +192 -0
  41. /package/src/motion/{presets.vibot.ts → presets.alt.ts} +0 -0
  42. /package/src/ui/{accordion.verve.tsx → accordion.alt.tsx} +0 -0
  43. /package/src/ui/{clear-input.vibot.tsx → clear-input.alt.tsx} +0 -0
  44. /package/src/ui/{input-group.verve.tsx → input-group.alt.tsx} +0 -0
  45. /package/src/ui/{scroll-area.verve.tsx → scroll-area.alt.tsx} +0 -0
@@ -0,0 +1,34 @@
1
+ /*
2
+ * @vvedantb/ui/tokens/css/utilities.css: Tailwind `@utility` helpers. Generated
3
+ * only when used, and they take variants (e.g. `max-sm:hit-target`).
4
+ */
5
+
6
+ /* Extends sub-40px controls to a 40x40px tap target without changing layout.
7
+ Only force `relative` when the element is otherwise static: absolute /
8
+ fixed / sticky already form a containing block for ::after. */
9
+ @utility hit-target {
10
+ &:not(.absolute):not(.fixed):not(.sticky) {
11
+ position: relative;
12
+ }
13
+ &::after {
14
+ content: "";
15
+ position: absolute;
16
+ inset: -8px;
17
+ }
18
+ }
19
+
20
+ /* Row actions that only appear on group-hover. Touch has no hover, so the
21
+ control stays visible on phones and hides only where hover exists. */
22
+ @utility reveal-on-hover {
23
+ opacity: 1;
24
+
25
+ @media (hover: hover) and (width >= theme(--breakpoint-sm)) {
26
+ opacity: 0;
27
+
28
+ &:is(:where(.group):hover *),
29
+ &:is(:where(.group):focus-within *),
30
+ &:focus-visible {
31
+ opacity: 1;
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * JS tokens for React Native. Values match `tokens.css`
3
+ * (oklch neutrals → hex, 1rem = 16).
4
+ */
5
+ export const tokens = {
6
+ color: {
7
+ background: { light: "#f6f6f6", dark: "#1f1f1f" },
8
+ foreground: { light: "#242426", dark: "#fafafa" },
9
+ muted: { light: "#737373", dark: "#b3b3b3" },
10
+ border: { light: "#e5e5e5", dark: "#474747" },
11
+ surface: { light: "#ffffff", dark: "#242426" },
12
+ surfaceSecondary: { light: "#f2f2f2", dark: "#2a2a2c" },
13
+ surfaceCard: { light: "#fafafa", dark: "#2a2a2c" },
14
+ separator: { light: "#ebebeb", dark: "#404040" },
15
+ white: "#ffffff",
16
+ black: "#000000",
17
+ hairline: {
18
+ light: "rgba(255, 255, 255, 0.7)",
19
+ dark: "rgba(255, 255, 255, 0.12)",
20
+ },
21
+ fill: {
22
+ light: "rgba(255, 255, 255, 0.86)",
23
+ dark: "rgba(28, 28, 30, 0.88)",
24
+ },
25
+ },
26
+ radius: {
27
+ lg: 16,
28
+ field: 20,
29
+ md: 14,
30
+ sm: 12,
31
+ card: 24,
32
+ pill: 999,
33
+ },
34
+ spacing: {
35
+ unit: 16,
36
+ },
37
+ } as const;
38
+
39
+ export type Tokens = typeof tokens;
@@ -0,0 +1,25 @@
1
+ /** Maps a CSS variable name (without `--`) to a Tailwind colour value. */
2
+ export type ColorFn = (name: string) => string;
3
+
4
+ type Colors = { [key: string]: string | Colors };
5
+ type Frames = Record<string, Record<string, string>>;
6
+
7
+ export interface TokensPreset {
8
+ darkMode: "class";
9
+ theme: {
10
+ extend: {
11
+ colors: Colors;
12
+ borderRadius: Record<string, string>;
13
+ boxShadow: Record<string, string>;
14
+ transitionTimingFunction: Record<string, string>;
15
+ screens: Record<string, string>;
16
+ keyframes: Record<string, Frames>;
17
+ animation: Record<string, string>;
18
+ };
19
+ };
20
+ }
21
+
22
+ export declare const cssVar: ColorFn;
23
+ export declare function tokensPreset(options?: { color?: ColorFn }): TokensPreset;
24
+ declare const preset: TokensPreset;
25
+ export default preset;
@@ -0,0 +1,177 @@
1
+ /**
2
+ * @vvedantb/ui/tokens/tailwind-preset: theme blocks shared by the apps that load
3
+ * a JS tailwind.config through `@config` (Tailwind v4 compat).
4
+ *
5
+ * import { tokensPreset } from "@vvedantb/ui/tokens/tailwind-preset";
6
+ * export default { presets: [tokensPreset()], theme: { extend: { ... } } };
7
+ *
8
+ * `color` maps a CSS variable name to a colour value. The default is
9
+ * `var(--name)`; pass a wrapper (e.g. color-mix with `<alpha-value>`) to
10
+ * keep an app's opacity-modifier behaviour. App-specific colours, fonts,
11
+ * plugins and content globs stay in the app config.
12
+ *
13
+ * Do not also import css/animations.css: these keyframes and animations are
14
+ * its config-side counterpart.
15
+ */
16
+
17
+ export const cssVar = (name) => `var(--${name})`;
18
+
19
+ export function tokensPreset({ color = cssVar } = {}) {
20
+ const v = color;
21
+ return {
22
+ darkMode: "class",
23
+ theme: {
24
+ extend: {
25
+ colors: {
26
+ border: v("border"),
27
+ backdrop: v("backdrop"),
28
+ background: v("background"),
29
+ foreground: v("foreground"),
30
+ danger: {
31
+ DEFAULT: v("danger"),
32
+ foreground: v("danger-foreground"),
33
+ ring: v("danger-ring"),
34
+ border: v("danger-border"),
35
+ },
36
+ default: {
37
+ DEFAULT: v("default"),
38
+ foreground: v("default-foreground"),
39
+ },
40
+ field: {
41
+ background: v("field-background"),
42
+ border: v("field-border"),
43
+ foreground: v("field-foreground"),
44
+ placeholder: v("field-placeholder"),
45
+ },
46
+ focus: {
47
+ DEFAULT: v("focus"),
48
+ ring: v("focus-ring"),
49
+ border: v("focus-border"),
50
+ },
51
+ overlay: {
52
+ DEFAULT: v("overlay"),
53
+ foreground: v("overlay-foreground"),
54
+ },
55
+ scrollbar: v("scrollbar"),
56
+ segment: {
57
+ DEFAULT: v("segment"),
58
+ foreground: v("segment-foreground"),
59
+ },
60
+ separator: v("separator"),
61
+ success: {
62
+ DEFAULT: v("success"),
63
+ foreground: v("success-foreground"),
64
+ },
65
+ surface: {
66
+ DEFAULT: v("surface"),
67
+ foreground: v("surface-foreground"),
68
+ secondary: v("surface-secondary"),
69
+ "secondary-foreground": v("surface-secondary-foreground"),
70
+ tertiary: v("surface-tertiary"),
71
+ "tertiary-foreground": v("surface-tertiary-foreground"),
72
+ card: v("surface-card"),
73
+ },
74
+ warning: {
75
+ DEFAULT: v("warning"),
76
+ foreground: v("warning-foreground"),
77
+ },
78
+ // Legacy shadcn names mapped to the same tokens.
79
+ primary: {
80
+ DEFAULT: v("accent"),
81
+ foreground: v("accent-foreground"),
82
+ },
83
+ destructive: {
84
+ DEFAULT: v("danger"),
85
+ foreground: v("danger-foreground"),
86
+ },
87
+ secondary: {
88
+ DEFAULT: v("default"),
89
+ foreground: v("default-foreground"),
90
+ },
91
+ input: v("border"),
92
+ popover: {
93
+ DEFAULT: v("overlay"),
94
+ foreground: v("overlay-foreground"),
95
+ },
96
+ card: {
97
+ DEFAULT: v("surface-card"),
98
+ foreground: v("surface-foreground"),
99
+ },
100
+ },
101
+ borderRadius: {
102
+ lg: "var(--radius)",
103
+ field: "var(--field-radius)",
104
+ md: "calc(var(--radius) - 2px)",
105
+ sm: "calc(var(--radius) - 4px)",
106
+ },
107
+ boxShadow: {
108
+ soft: "0 1px 2px rgba(16, 24, 40, 0.06), 0 10px 28px rgba(16, 24, 40, 0.06)",
109
+ panel:
110
+ "0 1px 2px rgba(16, 24, 40, 0.05), 0 16px 44px rgba(16, 24, 40, 0.1)",
111
+ insetSoft:
112
+ "inset 0 1px 0 rgba(255, 255, 255, 0.72), inset 0 -1px 0 rgba(16, 24, 40, 0.04)",
113
+ },
114
+ transitionTimingFunction: {
115
+ smooth: "cubic-bezier(0.22, 1, 0.36, 1)",
116
+ },
117
+ screens: {
118
+ xs: "475px",
119
+ sm: "640px",
120
+ md: "768px",
121
+ lg: "1024px",
122
+ xl: "1280px",
123
+ "2xl": "1536px",
124
+ },
125
+ keyframes: {
126
+ aurora: {
127
+ "0%": {
128
+ backgroundPosition: "0% 50%",
129
+ transform: "rotate(-5deg) scale(0.9)",
130
+ },
131
+ "25%": {
132
+ backgroundPosition: "50% 100%",
133
+ transform: "rotate(5deg) scale(1.1)",
134
+ },
135
+ "50%": {
136
+ backgroundPosition: "100% 50%",
137
+ transform: "rotate(-3deg) scale(0.95)",
138
+ },
139
+ "75%": {
140
+ backgroundPosition: "50% 0%",
141
+ transform: "rotate(3deg) scale(1.05)",
142
+ },
143
+ "100%": {
144
+ backgroundPosition: "0% 50%",
145
+ transform: "rotate(-5deg) scale(0.9)",
146
+ },
147
+ },
148
+ "fade-in-up": {
149
+ "0%": { opacity: "0", transform: "translateY(10px)" },
150
+ "100%": { opacity: "1", transform: "translateY(0)" },
151
+ },
152
+ indeterminate: {
153
+ "0%": { transform: "translateX(-100%)" },
154
+ "100%": { transform: "translateX(400%)" },
155
+ },
156
+ // Used by @vvedantb/ui accordion; each app sets its own
157
+ // `animation["accordion-down" | "accordion-up"]` timing.
158
+ "accordion-down": {
159
+ from: { height: "0" },
160
+ to: { height: "var(--radix-accordion-content-height)" },
161
+ },
162
+ "accordion-up": {
163
+ from: { height: "var(--radix-accordion-content-height)" },
164
+ to: { height: "0" },
165
+ },
166
+ },
167
+ animation: {
168
+ aurora: "aurora 8s ease-in-out infinite alternate",
169
+ "fade-in-up": "fade-in-up 0.4s ease-out forwards",
170
+ indeterminate: "indeterminate 1.5s ease-in-out infinite",
171
+ },
172
+ },
173
+ },
174
+ };
175
+ }
176
+
177
+ export default tokensPreset();
@@ -0,0 +1,192 @@
1
+ /*
2
+ * @vvedantb/ui/tokens — shared design tokens for vvedantb web apps.
3
+ *
4
+ * Import after Tailwind v4 in the app stylesheet:
5
+ *
6
+ * @import "tailwindcss";
7
+ * @import "@vvedantb/ui/tokens";
8
+ *
9
+ * This file does not import tailwindcss, plugins, marketing fonts or
10
+ * app navigation variables. Those stay in each app.
11
+ *
12
+ * Optional font: Instrument Sans. CSS requires @import url(...) to come
13
+ * before every other rule, so it cannot live here once this file is inlined
14
+ * after tailwindcss. Add it at the very top of the app stylesheet instead:
15
+ *
16
+ * @import url("https://fonts.googleapis.com/css2?family=Instrument+Sans:ital,wght@0,400..700;1,400..700&display=swap");
17
+ *
18
+ * Without it, --font-sans falls back to system-ui.
19
+ */
20
+
21
+ @custom-variant dark (&:where(.dark, .dark *));
22
+
23
+ @theme {
24
+ --color-border: var(--border);
25
+ --color-backdrop: var(--backdrop);
26
+ --color-background: var(--background);
27
+ --color-foreground: var(--foreground);
28
+ --color-accent: var(--accent);
29
+ --color-accent-foreground: var(--accent-foreground);
30
+ --color-danger: var(--danger);
31
+ --color-danger-foreground: var(--danger-foreground);
32
+ --color-danger-ring: var(--danger-ring);
33
+ --color-danger-border: var(--danger-border);
34
+ --color-default: var(--default);
35
+ --color-default-foreground: var(--default-foreground);
36
+ --color-field-background: var(--field-background);
37
+ --color-field-border: var(--field-border);
38
+ --color-field-foreground: var(--field-foreground);
39
+ --color-field-placeholder: var(--field-placeholder);
40
+ --color-focus: var(--focus);
41
+ --color-focus-ring: var(--focus-ring);
42
+ --color-focus-border: var(--focus-border);
43
+ --color-muted: var(--muted);
44
+ --color-overlay: var(--overlay);
45
+ --color-overlay-foreground: var(--overlay-foreground);
46
+ --color-scrollbar: var(--scrollbar);
47
+ --color-segment: var(--segment);
48
+ --color-segment-foreground: var(--segment-foreground);
49
+ --color-separator: var(--separator);
50
+ --color-success: var(--success);
51
+ --color-success-foreground: var(--success-foreground);
52
+ --color-surface: var(--surface);
53
+ --color-surface-foreground: var(--surface-foreground);
54
+ --color-surface-secondary: var(--surface-secondary);
55
+ --color-surface-secondary-foreground: var(--surface-secondary-foreground);
56
+ --color-surface-tertiary: var(--surface-tertiary);
57
+ --color-surface-tertiary-foreground: var(--surface-tertiary-foreground);
58
+ --color-surface-card: var(--surface-card);
59
+ --color-warning: var(--warning);
60
+ --color-warning-foreground: var(--warning-foreground);
61
+
62
+ --radius-lg: var(--radius);
63
+ --radius-field: var(--field-radius);
64
+ --radius-md: calc(var(--radius) - 2px);
65
+ --radius-sm: calc(var(--radius) - 4px);
66
+
67
+ --shadow-overlay: var(--overlay-shadow);
68
+ --shadow-soft:
69
+ 0 1px 2px rgba(16, 24, 40, 0.06), 0 10px 28px rgba(16, 24, 40, 0.06);
70
+ --shadow-panel:
71
+ 0 1px 2px rgba(16, 24, 40, 0.05), 0 16px 44px rgba(16, 24, 40, 0.1);
72
+ --shadow-insetSoft:
73
+ inset 0 1px 0 rgba(255, 255, 255, 0.72),
74
+ inset 0 -1px 0 rgba(16, 24, 40, 0.04);
75
+
76
+ --ease-smooth: cubic-bezier(0.22, 1, 0.36, 1);
77
+
78
+ --font-sans: "Instrument Sans", system-ui, sans-serif;
79
+ --font-instrumentSans: "Instrument Sans", system-ui, sans-serif;
80
+ }
81
+
82
+ @layer base {
83
+ :root,
84
+ .light,
85
+ .default,
86
+ [data-theme="light"],
87
+ [data-theme="default"] {
88
+ color-scheme: light;
89
+ --accent: oklch(21.03% 0 0);
90
+ --accent-foreground: oklch(99.11% 0 0);
91
+ --backdrop: rgb(0 0 0 / 0.5);
92
+ --background: oklch(97.02% 0 0);
93
+ --border: oklch(90% 0 0);
94
+ --danger: oklch(65.32% 0.2335 25.74);
95
+ --danger-foreground: oklch(99.11% 0 0);
96
+ --default: oklch(94% 0 0);
97
+ --default-foreground: oklch(21.03% 0 0);
98
+ --field-background: oklch(100% 0 0);
99
+ --field-border: transparent;
100
+ --field-foreground: oklch(21.03% 0 0);
101
+ --field-placeholder: oklch(55.17% 0 0);
102
+ --focus: var(--accent);
103
+ --focus-ring: color-mix(in oklch, var(--foreground) 16%, transparent);
104
+ --focus-border: color-mix(in oklch, var(--foreground) 28%, var(--border));
105
+ --danger-ring: color-mix(in oklch, var(--danger) 22%, transparent);
106
+ --danger-border: color-mix(in oklch, var(--danger) 55%, var(--border));
107
+ --foreground: oklch(21.03% 0 0);
108
+ --muted: oklch(55.17% 0 0);
109
+ --overlay: oklch(100% 0 0);
110
+ --overlay-foreground: oklch(21.03% 0 0);
111
+ --overlay-shadow:
112
+ 0 2px 8px 0 rgb(0 0 0 / 0.06), 0 -6px 12px 0 rgb(0 0 0 / 0.03),
113
+ 0 14px 28px 0 rgb(0 0 0 / 0.12);
114
+ --scrollbar: oklch(87.1% 0 0);
115
+ --segment: oklch(100% 0 0);
116
+ --segment-foreground: oklch(21.03% 0 0);
117
+ --separator: oklch(92% 0 0);
118
+ --success: oklch(73.29% 0.1941 150.81);
119
+ --success-foreground: oklch(21.03% 0.0059 150.81);
120
+ --surface: oklch(100% 0 0);
121
+ --surface-foreground: oklch(21.03% 0 0);
122
+ --surface-secondary: oklch(95.24% 0 0);
123
+ --surface-secondary-foreground: oklch(21.03% 0 0);
124
+ --surface-tertiary: oklch(93.73% 0 0);
125
+ --surface-tertiary-foreground: oklch(21.03% 0 0);
126
+ --surface-card: oklch(98% 0 0);
127
+ --warning: oklch(78.19% 0.159 72.33);
128
+ --warning-foreground: oklch(21.03% 0.0059 72.33);
129
+ --radius: 1rem;
130
+ --field-radius: 1.25rem;
131
+ }
132
+
133
+ .dark,
134
+ [data-theme="dark"] {
135
+ color-scheme: dark;
136
+ --accent: oklch(99.11% 0 0);
137
+ --accent-foreground: oklch(21.03% 0 0);
138
+ --backdrop: rgb(0 0 0 / 0.6);
139
+ --background: oklch(12% 0 0);
140
+ --border: oklch(28% 0 0);
141
+ --danger: oklch(59.4% 0.1973 24.63);
142
+ --danger-foreground: oklch(99.11% 0 0);
143
+ --default: oklch(27.4% 0 0);
144
+ --default-foreground: oklch(99.11% 0 0);
145
+ --field-background: oklch(21.03% 0 0);
146
+ --field-border: transparent;
147
+ --field-foreground: oklch(99.11% 0 0);
148
+ --field-placeholder: oklch(70.5% 0 0);
149
+ --focus: var(--accent);
150
+ --focus-ring: color-mix(in oklch, var(--foreground) 16%, transparent);
151
+ --focus-border: color-mix(in oklch, var(--foreground) 28%, var(--border));
152
+ --danger-ring: color-mix(in oklch, var(--danger) 22%, transparent);
153
+ --danger-border: color-mix(in oklch, var(--danger) 55%, var(--border));
154
+ --foreground: oklch(99.11% 0 0);
155
+ --muted: oklch(70.5% 0 0);
156
+ --overlay: oklch(21.03% 0 0);
157
+ --overlay-foreground: oklch(99.11% 0 0);
158
+ --overlay-shadow:
159
+ 0 0 0 1px rgb(255 255 255 / 0.08), 0 2px 8px 0 rgb(0 0 0 / 0.35),
160
+ 0 14px 28px 0 rgb(0 0 0 / 0.5);
161
+ --scrollbar: oklch(70.5% 0 0);
162
+ --segment: oklch(39.64% 0 0);
163
+ --segment-foreground: oklch(99.11% 0 0);
164
+ --separator: oklch(25% 0 0);
165
+ --success: oklch(73.29% 0.1941 150.81);
166
+ --success-foreground: oklch(21.03% 0.0059 150.81);
167
+ --surface: oklch(21.03% 0 0);
168
+ --surface-foreground: oklch(99.11% 0 0);
169
+ --surface-secondary: oklch(25.7% 0.0023 0);
170
+ --surface-secondary-foreground: oklch(99.11% 0 0);
171
+ --surface-tertiary: oklch(27.21% 0.0023 0);
172
+ --surface-tertiary-foreground: oklch(99.11% 0 0);
173
+ --surface-card: oklch(25.7% 0.0023 0);
174
+ --warning: oklch(82.03% 0.1392 76.34);
175
+ --warning-foreground: oklch(21.03% 0.0059 76.34);
176
+ --radius: 1rem;
177
+ --field-radius: 1.25rem;
178
+ }
179
+ }
180
+
181
+ @layer base {
182
+ * {
183
+ @apply border-border;
184
+ }
185
+
186
+ body {
187
+ @apply bg-background text-foreground font-sans antialiased;
188
+ text-rendering: optimizeLegibility;
189
+ -webkit-font-smoothing: antialiased;
190
+ -moz-osx-font-smoothing: grayscale;
191
+ }
192
+ }
File without changes