@n3rd-ai/ui 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  // src/primitives/ascii-border.ts
2
3
  var BORDER_CHARS = {
3
4
  single: {
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { createContext, useState, useCallback, useContext } from 'react';
2
3
  import { jsxs, jsx } from 'react/jsx-runtime';
3
4
 
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  export { useToast } from '../chunk-MZO6ECNX.js';
2
3
  import { useState, useRef, useEffect, useCallback } from 'react';
3
4
 
@@ -9,8 +10,9 @@ function useTypewriter({ text, speed = 50, delay = 0, onComplete }) {
9
10
  if (hasRun.current) return;
10
11
  hasRun.current = true;
11
12
  let index = 0;
13
+ let interval;
12
14
  const timeout = setTimeout(() => {
13
- const interval = setInterval(() => {
15
+ interval = setInterval(() => {
14
16
  if (index < text.length) {
15
17
  setDisplayed(text.slice(0, index + 1));
16
18
  index++;
@@ -20,9 +22,11 @@ function useTypewriter({ text, speed = 50, delay = 0, onComplete }) {
20
22
  onComplete?.();
21
23
  }
22
24
  }, speed);
23
- return () => clearInterval(interval);
24
25
  }, delay);
25
- return () => clearTimeout(timeout);
26
+ return () => {
27
+ clearTimeout(timeout);
28
+ clearInterval(interval);
29
+ };
26
30
  }, [text, speed, delay, onComplete]);
27
31
  return { displayed, done };
28
32
  }
@@ -31,10 +35,11 @@ function useKeyboard(shortcuts) {
31
35
  (e) => {
32
36
  for (const shortcut of shortcuts) {
33
37
  const keyMatch = e.key.toLowerCase() === shortcut.key.toLowerCase();
34
- const ctrlMatch = (shortcut.ctrl ?? false) === (e.ctrlKey || e.metaKey);
38
+ const ctrlMatch = (shortcut.ctrl ?? false) === e.ctrlKey;
39
+ const metaMatch = (shortcut.meta ?? false) === e.metaKey;
35
40
  const shiftMatch = (shortcut.shift ?? false) === e.shiftKey;
36
41
  const altMatch = (shortcut.alt ?? false) === e.altKey;
37
- if (keyMatch && ctrlMatch && shiftMatch && altMatch) {
42
+ if (keyMatch && ctrlMatch && metaMatch && shiftMatch && altMatch) {
38
43
  e.preventDefault();
39
44
  shortcut.handler(e);
40
45
  return;
package/dist/index.css CHANGED
@@ -1,104 +1,125 @@
1
- /* src/components/layout/Box.module.css */
2
- .box {
1
+ /* src/globals.css */
2
+ @keyframes n3rd-cursor-blink {
3
+ 0%, 100% {
4
+ opacity: 1;
5
+ }
6
+ 50% {
7
+ opacity: 0;
8
+ }
9
+ }
10
+ @keyframes n3rd-fade-in {
11
+ from {
12
+ opacity: 0;
13
+ transform: translateY(4px);
14
+ }
15
+ to {
16
+ opacity: 1;
17
+ transform: translateY(0);
18
+ }
19
+ }
20
+
21
+ /* src/components/layout/Box.css */
22
+ .n3rd-box {
3
23
  font-family: var(--n3rd-font);
4
24
  color: var(--box-border-color, var(--n3rd-border-default));
5
25
  width: 100%;
26
+ line-height: 1;
6
27
  }
7
- .borderTop,
8
- .borderBottom {
28
+ .n3rd-box-border-top,
29
+ .n3rd-box-border-bottom {
9
30
  display: flex;
10
31
  align-items: center;
11
32
  white-space: nowrap;
12
33
  overflow: hidden;
34
+ letter-spacing: -0.05em;
13
35
  }
14
- .borderChar {
36
+ .n3rd-box-border-char {
15
37
  flex-shrink: 0;
16
38
  }
17
- .borderLine {
39
+ .n3rd-box-border-line {
18
40
  flex: 1;
19
41
  overflow: hidden;
20
42
  white-space: nowrap;
21
43
  }
22
- .borderLine::before {
23
- content: "\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500\2500";
24
- display: block;
25
- overflow: hidden;
26
- }
27
- .title {
44
+ .n3rd-box-title {
28
45
  flex-shrink: 0;
29
46
  color: var(--n3rd-text-secondary);
30
47
  font-size: var(--n3rd-text-sm);
48
+ letter-spacing: normal;
31
49
  }
32
- .content {
50
+ .n3rd-box-content {
33
51
  display: flex;
52
+ letter-spacing: -0.05em;
34
53
  }
35
- .borderSide {
54
+ .n3rd-box-border-side {
36
55
  flex-shrink: 0;
37
56
  }
38
- .inner {
57
+ .n3rd-box-inner {
39
58
  flex: 1;
40
59
  padding: var(--box-padding, var(--n3rd-space-4));
41
60
  color: var(--n3rd-text-primary);
61
+ letter-spacing: normal;
62
+ line-height: var(--n3rd-line-height);
42
63
  }
43
64
 
44
- /* src/components/display/Footer.module.css */
45
- .footer {
65
+ /* src/components/display/Footer.css */
66
+ .n3rd-footer {
46
67
  font-family: var(--n3rd-font);
47
68
  text-align: center;
48
69
  padding: var(--n3rd-space-8) 0 var(--n3rd-space-6);
49
70
  overflow: hidden;
50
71
  }
51
- .branding {
72
+ .n3rd-footer-branding {
52
73
  color: var(--n3rd-text-tertiary);
53
74
  font-size: var(--n3rd-text-xs);
54
75
  margin-bottom: var(--n3rd-space-6);
55
76
  letter-spacing: 0.1em;
56
77
  }
57
- .starfield {
78
+ .n3rd-footer-starfield {
58
79
  color: var(--n3rd-accent-lavender);
59
80
  font-size: var(--n3rd-text-sm);
60
81
  line-height: 1.8;
61
82
  opacity: 0.4;
62
83
  margin-bottom: var(--n3rd-space-4);
63
84
  }
64
- .starRow {
85
+ .n3rd-footer-star-row {
65
86
  white-space: pre;
66
87
  }
67
- .sunset {
88
+ .n3rd-footer-sunset {
68
89
  display: flex;
69
90
  flex-direction: column;
70
91
  align-items: center;
71
92
  margin-bottom: 0;
72
93
  }
73
- .sunLine {
94
+ .n3rd-footer-sun-line {
74
95
  height: 6px;
75
96
  border-radius: 0;
76
97
  margin: 1px 0;
77
98
  }
78
- .sunGap {
99
+ .n3rd-footer-sun-gap {
79
100
  height: 2px;
80
101
  }
81
- .horizon {
102
+ .n3rd-footer-horizon {
82
103
  display: flex;
83
104
  flex-direction: column;
84
105
  align-items: center;
85
106
  margin-bottom: var(--n3rd-space-6);
86
107
  }
87
- .horizonBright {
108
+ .n3rd-footer-horizon-bright {
88
109
  width: 100%;
89
110
  height: 1px;
90
111
  background: var(--n3rd-accent-rose);
91
112
  opacity: 0.8;
92
113
  margin-bottom: var(--n3rd-space-2);
93
114
  }
94
- .reflection {
115
+ .n3rd-footer-reflection {
95
116
  width: 80%;
96
117
  height: 1px;
97
118
  background: var(--n3rd-accent-pink);
98
119
  opacity: 0.3;
99
120
  margin-bottom: var(--n3rd-space-2);
100
121
  }
101
- .reflectionDashed {
122
+ .n3rd-footer-reflection-dashed {
102
123
  width: 70%;
103
124
  height: 1px;
104
125
  background:
@@ -111,29 +132,29 @@
111
132
  opacity: 0.2;
112
133
  margin-bottom: var(--n3rd-space-3);
113
134
  }
114
- .tagline {
135
+ .n3rd-footer-tagline {
115
136
  color: var(--n3rd-text-secondary);
116
137
  font-size: var(--n3rd-text-sm);
117
138
  font-weight: 700;
118
139
  letter-spacing: 0.15em;
119
140
  margin-bottom: var(--n3rd-space-4);
120
141
  }
121
- .links {
142
+ .n3rd-footer-links {
122
143
  font-size: var(--n3rd-text-xs);
123
144
  }
124
- .link {
145
+ .n3rd-footer-link {
125
146
  color: var(--n3rd-text-tertiary);
126
147
  text-decoration: none;
127
148
  }
128
- .link:hover {
149
+ .n3rd-footer-link:hover {
129
150
  color: var(--n3rd-accent-cyan);
130
151
  }
131
- .sep {
152
+ .n3rd-footer-sep {
132
153
  color: var(--n3rd-border-muted);
133
154
  }
134
155
 
135
- /* src/components/input/Button.module.css */
136
- .button {
156
+ /* src/components/input/Button.css */
157
+ .n3rd-btn {
137
158
  display: inline-flex;
138
159
  align-items: center;
139
160
  gap: var(--n3rd-space-1);
@@ -149,63 +170,57 @@
149
170
  background: none;
150
171
  padding: var(--n3rd-space-1) var(--n3rd-space-2);
151
172
  }
152
- .primary {
173
+ .n3rd-btn-primary {
153
174
  color: var(--n3rd-accent-primary);
154
175
  }
155
- .primary:hover {
176
+ .n3rd-btn-primary:hover {
156
177
  background: var(--n3rd-accent-primary);
157
178
  color: var(--n3rd-bg-primary);
158
179
  }
159
- .secondary {
180
+ .n3rd-btn-secondary {
160
181
  color: var(--n3rd-text-secondary);
161
182
  }
162
- .secondary:hover {
183
+ .n3rd-btn-secondary:hover {
163
184
  color: var(--n3rd-text-primary);
164
185
  }
165
- .danger {
186
+ .n3rd-btn-danger {
166
187
  color: var(--n3rd-accent-danger);
167
188
  }
168
- .danger:hover {
189
+ .n3rd-btn-danger:hover {
169
190
  background: var(--n3rd-accent-danger);
170
191
  color: var(--n3rd-bg-primary);
171
192
  }
172
- .ghost {
193
+ .n3rd-btn-ghost {
173
194
  color: var(--n3rd-text-secondary);
174
195
  padding: var(--n3rd-space-1) 0;
175
196
  }
176
- .ghost:hover {
197
+ .n3rd-btn-ghost:hover {
177
198
  color: var(--n3rd-text-primary);
178
199
  }
179
- .disabled {
200
+ .n3rd-btn-disabled {
180
201
  opacity: 0.4;
181
202
  cursor: not-allowed;
182
203
  pointer-events: none;
183
204
  }
184
- .loading {
205
+ .n3rd-btn-loading {
185
206
  cursor: wait;
186
207
  }
187
- .spinner {
208
+ .n3rd-btn-spinner {
188
209
  display: inline-block;
189
- animation: n3rd-spin 0.8s steps(10) infinite;
190
210
  margin-right: var(--n3rd-space-1);
191
211
  }
192
- @keyframes n3rd-spin {
193
- to {
194
- transform: rotate(360deg);
195
- }
196
- }
197
212
 
198
- /* src/components/input/Input.module.css */
199
- .wrapper {
213
+ /* src/components/input/Input.css */
214
+ .n3rd-input-wrapper {
200
215
  font-family: var(--n3rd-font);
201
216
  }
202
- .label {
217
+ .n3rd-input-label {
203
218
  display: block;
204
219
  color: var(--n3rd-text-secondary);
205
220
  font-size: var(--n3rd-text-sm);
206
221
  margin-bottom: var(--n3rd-space-1);
207
222
  }
208
- .field {
223
+ .n3rd-input-field {
209
224
  display: flex;
210
225
  align-items: center;
211
226
  gap: var(--n3rd-space-2);
@@ -214,24 +229,24 @@
214
229
  border: 1px solid var(--n3rd-border-default);
215
230
  cursor: text;
216
231
  }
217
- .focused {
232
+ .n3rd-input-field-focused {
218
233
  border-color: var(--n3rd-border-focus);
219
234
  }
220
- .disabled {
235
+ .n3rd-input-field-disabled {
221
236
  opacity: 0.5;
222
237
  cursor: not-allowed;
223
238
  }
224
- .prefix {
239
+ .n3rd-input-prefix {
225
240
  color: var(--n3rd-accent-primary);
226
241
  flex-shrink: 0;
227
242
  user-select: none;
228
243
  }
229
- .inputWrap {
244
+ .n3rd-input-wrap {
230
245
  flex: 1;
231
246
  display: flex;
232
247
  align-items: center;
233
248
  }
234
- .input {
249
+ .n3rd-input {
235
250
  flex: 1;
236
251
  font-family: var(--n3rd-font);
237
252
  font-size: var(--n3rd-text-base);
@@ -242,12 +257,12 @@
242
257
  padding: 0;
243
258
  caret-color: transparent;
244
259
  }
245
- .input::placeholder {
260
+ .n3rd-input::placeholder {
246
261
  color: var(--n3rd-text-tertiary);
247
262
  }
248
263
 
249
- /* src/components/nav/Nav.module.css */
250
- .nav {
264
+ /* src/components/nav/Nav.css */
265
+ .n3rd-nav {
251
266
  display: flex;
252
267
  align-items: center;
253
268
  padding: var(--n3rd-space-3) var(--n3rd-space-4);
@@ -255,27 +270,27 @@
255
270
  font-size: var(--n3rd-text-sm);
256
271
  border-bottom: 1px solid var(--n3rd-border-muted);
257
272
  }
258
- .items {
273
+ .n3rd-nav-items {
259
274
  display: flex;
260
275
  align-items: center;
261
276
  gap: var(--n3rd-space-2);
262
277
  flex-wrap: wrap;
263
278
  }
264
- .item {
279
+ .n3rd-nav-item {
265
280
  color: var(--n3rd-text-secondary);
266
281
  text-decoration: none;
267
282
  transition: color 0.15s;
268
283
  white-space: nowrap;
269
284
  }
270
- .item:hover {
285
+ .n3rd-nav-item:hover {
271
286
  color: var(--n3rd-text-primary);
272
287
  }
273
- .active {
288
+ .n3rd-nav-active {
274
289
  color: var(--n3rd-accent-primary);
275
290
  }
276
- .active:hover {
291
+ .n3rd-nav-active:hover {
277
292
  color: var(--n3rd-accent-primary);
278
293
  }
279
- .indicator {
294
+ .n3rd-nav-indicator {
280
295
  color: var(--n3rd-accent-primary);
281
296
  }
package/dist/index.d.ts CHANGED
@@ -3,7 +3,6 @@ import { ReactNode, CSSProperties, MouseEvent } from 'react';
3
3
  import { B as BorderStyle } from './ascii-border-DpKIQMLP.js';
4
4
  export { a as BORDER_CHARS, g as getBorderChars } from './ascii-border-DpKIQMLP.js';
5
5
  export { T as ToastProvider, u as useToast } from './Toast-qHlZE8FW.js';
6
- import * as next_dist_compiled__next_font from 'next/dist/compiled/@next/font';
7
6
 
8
7
  type Accent$3 = 'primary' | 'success' | 'warning' | 'danger' | 'info';
9
8
  type Padding = 'none' | 'sm' | 'md' | 'lg';
@@ -276,15 +275,56 @@ interface ScanlineProps {
276
275
  }
277
276
  declare function Scanline({ opacity }: ScanlineProps): react_jsx_runtime.JSX.Element;
278
277
 
278
+ /**
279
+ * 5-line block-letter font for ASCII art logos.
280
+ * Each glyph is an array of 5 strings, all padded to the same width.
281
+ */
282
+ /**
283
+ * Render a string as multi-line ASCII block art.
284
+ * Unknown characters are replaced with a single-width space.
285
+ */
286
+ declare function renderAsciiText(input: string): string;
287
+ /**
288
+ * Render as an array of lines (useful for per-line styling).
289
+ */
290
+ declare function renderAsciiLines(input: string): string[];
291
+
279
292
  interface N3rdProviderProps {
280
293
  children: ReactNode;
281
- theme?: 'unicorn' | 'classic' | 'retro' | 'paper';
282
294
  scanlines?: boolean;
283
295
  toastDuration?: number;
284
296
  }
285
297
  declare function N3rdProvider({ children, scanlines, toastDuration, }: N3rdProviderProps): react_jsx_runtime.JSX.Element;
286
298
 
287
- declare const jetbrainsMono: next_dist_compiled__next_font.NextFontWithVariable;
288
- declare const N3rdFonts: next_dist_compiled__next_font.NextFontWithVariable;
299
+ /**
300
+ * Font-family value matching the --n3rd-font CSS variable.
301
+ * Use this when you need to reference the font stack in JS.
302
+ *
303
+ * For most use cases, just import a theme preset CSS:
304
+ * import '@n3rd-ai/ui/theme/unicorn.css'
305
+ * import '@n3rd-ai/ui/theme/fonts.css' // optional: loads JetBrains Mono from CDN
306
+ *
307
+ * The CSS variable --n3rd-font is set globally by tokens.css.
308
+ */
309
+ declare const N3RD_FONT_FAMILY = "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', 'Consolas', 'Courier New', monospace";
310
+ /**
311
+ * @deprecated No longer needed — theme presets apply the font via CSS variables.
312
+ * Kept for backwards compatibility with v0.1.x.
313
+ */
314
+ declare const N3rdFonts: {
315
+ className: string;
316
+ variable: string;
317
+ style: {
318
+ fontFamily: string;
319
+ };
320
+ };
321
+ /** @deprecated Use N3RD_FONT_FAMILY instead. */
322
+ declare const jetbrainsMono: {
323
+ className: string;
324
+ variable: string;
325
+ style: {
326
+ fontFamily: string;
327
+ };
328
+ };
289
329
 
290
- export { Alert, type AlertProps, Badge, type BadgeProps, BorderStyle, Box, type BoxProps, Button, type ButtonProps, Code, type CodeProps, Cursor, type CursorProps, type CursorStyle, Divider, type DividerProps, Footer, type FooterProps, Grid, type GridProps, Heading, type HeadingProps, Input, type InputProps, List, type ListProps, Logo, type LogoProps, Metric, type MetricProps, N3rdFonts, N3rdProvider, type N3rdProviderProps, Nav, type NavProps, Page, type PageProps, Progress, type ProgressProps, Row, type RowProps, Scanline, type ScanlineProps, Skeleton, type SkeletonProps, Stack, type StackProps, StatusLine, type StatusLineProps, Table, type TableProps, Text, type TextProps, Typewriter, type TypewriterProps, jetbrainsMono };
330
+ export { Alert, type AlertProps, Badge, type BadgeProps, BorderStyle, Box, type BoxProps, Button, type ButtonProps, Code, type CodeProps, Cursor, type CursorProps, type CursorStyle, Divider, type DividerProps, Footer, type FooterProps, Grid, type GridProps, Heading, type HeadingProps, Input, type InputProps, List, type ListProps, Logo, type LogoProps, Metric, type MetricProps, N3RD_FONT_FAMILY, N3rdFonts, N3rdProvider, type N3rdProviderProps, Nav, type NavProps, Page, type PageProps, Progress, type ProgressProps, Row, type RowProps, Scanline, type ScanlineProps, Skeleton, type SkeletonProps, Stack, type StackProps, StatusLine, type StatusLineProps, Table, type TableProps, Text, type TextProps, Typewriter, type TypewriterProps, jetbrainsMono, renderAsciiLines, renderAsciiText };
package/dist/index.js CHANGED
@@ -1,13 +1,12 @@
1
+ "use client";
2
+ import "./index.css";
1
3
  import { ToastProvider } from './chunk-MZO6ECNX.js';
2
4
  export { ToastProvider, useToast } from './chunk-MZO6ECNX.js';
3
5
  import { getBorderChars } from './chunk-CBVIEAN7.js';
4
6
  export { BORDER_CHARS, getBorderChars } from './chunk-CBVIEAN7.js';
5
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
- import { useState, useRef, useEffect } from 'react';
7
- import localFont from 'next/font/local';
8
+ import { useState, useEffect, useRef } from 'react';
8
9
 
9
- // src/components/layout/Box.module.css
10
- var Box_default = {};
11
10
  var PADDING_MAP = {
12
11
  none: "0",
13
12
  sm: "var(--n3rd-space-2)",
@@ -33,25 +32,25 @@ function Box({
33
32
  "--box-border-color": accentColor,
34
33
  "--box-padding": PADDING_MAP[padding]
35
34
  };
36
- return /* @__PURE__ */ jsxs("div", { className: `${Box_default.box} ${className ?? ""}`, style: boxStyle, children: [
37
- /* @__PURE__ */ jsxs("div", { className: Box_default.borderTop, "aria-hidden": "true", children: [
38
- /* @__PURE__ */ jsx("span", { className: Box_default.borderChar, children: chars.topLeft }),
35
+ return /* @__PURE__ */ jsxs("div", { className: `n3rd-box ${className ?? ""}`, style: boxStyle, children: [
36
+ /* @__PURE__ */ jsxs("div", { className: "n3rd-box-border-top", "aria-hidden": "true", children: [
37
+ /* @__PURE__ */ jsx("span", { className: "n3rd-box-border-char", children: chars.topLeft }),
39
38
  title && /* @__PURE__ */ jsxs(Fragment, { children: [
40
- /* @__PURE__ */ jsx("span", { className: Box_default.borderChar, children: chars.horizontal }),
41
- /* @__PURE__ */ jsx("span", { className: Box_default.title, children: ` ${title} ` })
39
+ /* @__PURE__ */ jsx("span", { className: "n3rd-box-border-char", children: chars.horizontal }),
40
+ /* @__PURE__ */ jsx("span", { className: "n3rd-box-title", children: ` ${title} ` })
42
41
  ] }),
43
- /* @__PURE__ */ jsx("span", { className: Box_default.borderLine, children: chars.horizontal }),
44
- /* @__PURE__ */ jsx("span", { className: Box_default.borderChar, children: chars.topRight })
42
+ /* @__PURE__ */ jsx("span", { className: "n3rd-box-border-line", children: chars.horizontal.repeat(200) }),
43
+ /* @__PURE__ */ jsx("span", { className: "n3rd-box-border-char", children: chars.topRight })
45
44
  ] }),
46
- /* @__PURE__ */ jsxs("div", { className: Box_default.content, children: [
47
- /* @__PURE__ */ jsx("span", { className: Box_default.borderSide, "aria-hidden": "true", children: chars.vertical }),
48
- /* @__PURE__ */ jsx("div", { className: Box_default.inner, children }),
49
- /* @__PURE__ */ jsx("span", { className: Box_default.borderSide, "aria-hidden": "true", children: chars.vertical })
45
+ /* @__PURE__ */ jsxs("div", { className: "n3rd-box-content", children: [
46
+ /* @__PURE__ */ jsx("span", { className: "n3rd-box-border-side", "aria-hidden": "true", children: chars.vertical }),
47
+ /* @__PURE__ */ jsx("div", { className: "n3rd-box-inner", children }),
48
+ /* @__PURE__ */ jsx("span", { className: "n3rd-box-border-side", "aria-hidden": "true", children: chars.vertical })
50
49
  ] }),
51
- /* @__PURE__ */ jsxs("div", { className: Box_default.borderBottom, "aria-hidden": "true", children: [
52
- /* @__PURE__ */ jsx("span", { className: Box_default.borderChar, children: chars.bottomLeft }),
53
- /* @__PURE__ */ jsx("span", { className: Box_default.borderLine, children: chars.horizontal }),
54
- /* @__PURE__ */ jsx("span", { className: Box_default.borderChar, children: chars.bottomRight })
50
+ /* @__PURE__ */ jsxs("div", { className: "n3rd-box-border-bottom", "aria-hidden": "true", children: [
51
+ /* @__PURE__ */ jsx("span", { className: "n3rd-box-border-char", children: chars.bottomLeft }),
52
+ /* @__PURE__ */ jsx("span", { className: "n3rd-box-border-line", children: chars.horizontal.repeat(200) }),
53
+ /* @__PURE__ */ jsx("span", { className: "n3rd-box-border-char", children: chars.bottomRight })
55
54
  ] })
56
55
  ] });
57
56
  }
@@ -217,8 +216,18 @@ function Text({
217
216
  ...style
218
217
  };
219
218
  return /* @__PURE__ */ jsxs(Tag, { className, style: textStyle, children: [
220
- prefix && /* @__PURE__ */ jsx("span", { style: { color: "var(--n3rd-text-secondary)", marginRight: "var(--n3rd-space-1)" }, children: prefix }),
221
- children
219
+ prefix && /* @__PURE__ */ jsx(
220
+ "span",
221
+ {
222
+ style: {
223
+ color: "var(--n3rd-text-secondary)",
224
+ marginRight: "var(--n3rd-space-1)",
225
+ ...gradient && { WebkitTextFillColor: "var(--n3rd-text-secondary)" }
226
+ },
227
+ children: prefix
228
+ }
229
+ ),
230
+ gradient ? /* @__PURE__ */ jsx("span", { children }) : children
222
231
  ] });
223
232
  }
224
233
  var SIZE_MAP2 = {
@@ -262,8 +271,18 @@ function Heading({
262
271
  ...style
263
272
  };
264
273
  return /* @__PURE__ */ jsxs(Tag, { className, style: headingStyle, children: [
265
- prefix && /* @__PURE__ */ jsx("span", { style: { color: "var(--n3rd-text-tertiary)", marginRight: "var(--n3rd-space-2)" }, children: PREFIX_MAP[level] }),
266
- children
274
+ prefix && /* @__PURE__ */ jsx(
275
+ "span",
276
+ {
277
+ style: {
278
+ color: "var(--n3rd-text-tertiary)",
279
+ marginRight: "var(--n3rd-space-2)",
280
+ ...gradient && { WebkitTextFillColor: "var(--n3rd-text-tertiary)" }
281
+ },
282
+ children: PREFIX_MAP[level]
283
+ }
284
+ ),
285
+ gradient ? /* @__PURE__ */ jsx("span", { children }) : children
267
286
  ] });
268
287
  }
269
288
  var VARIANT_MAP = {
@@ -451,24 +470,98 @@ function List({ items, bullet = ">", className, style }) {
451
470
  i
452
471
  )) });
453
472
  }
473
+
474
+ // src/primitives/ascii-font.ts
475
+ var GLYPHS = {
476
+ A: [" \u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588"],
477
+ B: ["\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588 "],
478
+ C: [" \u2588\u2588\u2588\u2588\u2588", "\u2588\u2588 ", "\u2588\u2588 ", "\u2588\u2588 ", " \u2588\u2588\u2588\u2588\u2588"],
479
+ D: ["\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588 ", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588 ", "\u2588\u2588\u2588\u2588 "],
480
+ E: ["\u2588\u2588\u2588\u2588\u2588\u2588", "\u2588\u2588 ", "\u2588\u2588\u2588\u2588 ", "\u2588\u2588 ", "\u2588\u2588\u2588\u2588\u2588\u2588"],
481
+ F: ["\u2588\u2588\u2588\u2588\u2588\u2588", "\u2588\u2588 ", "\u2588\u2588\u2588\u2588 ", "\u2588\u2588 ", "\u2588\u2588 "],
482
+ G: [" \u2588\u2588\u2588\u2588 ", "\u2588\u2588 ", "\u2588\u2588 \u2588\u2588\u2588", "\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588 "],
483
+ H: ["\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588"],
484
+ I: ["\u2588\u2588\u2588\u2588", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 ", "\u2588\u2588\u2588\u2588"],
485
+ J: [" \u2588\u2588\u2588\u2588", " \u2588\u2588", " \u2588\u2588", "\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588 "],
486
+ K: ["\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588 ", "\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588 ", "\u2588\u2588 \u2588\u2588"],
487
+ L: ["\u2588\u2588 ", "\u2588\u2588 ", "\u2588\u2588 ", "\u2588\u2588 ", "\u2588\u2588\u2588\u2588\u2588\u2588"],
488
+ M: ["\u2588\u2588 \u2588\u2588", "\u2588\u2588\u2588 \u2588\u2588\u2588", "\u2588\u2588 \u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588"],
489
+ N: ["\u2588\u2588 \u2588\u2588", "\u2588\u2588\u2588 \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588\u2588", "\u2588\u2588 \u2588\u2588\u2588", "\u2588\u2588 \u2588\u2588"],
490
+ O: [" \u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588 "],
491
+ P: ["\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 ", "\u2588\u2588 "],
492
+ Q: [" \u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588 ", " \u2588\u2588 \u2588\u2588"],
493
+ R: ["\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588 ", "\u2588\u2588 \u2588\u2588"],
494
+ S: [" \u2588\u2588\u2588\u2588\u2588", "\u2588\u2588 ", " \u2588\u2588\u2588\u2588 ", " \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588 "],
495
+ T: ["\u2588\u2588\u2588\u2588\u2588\u2588", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 "],
496
+ U: ["\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588 "],
497
+ V: ["\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588 ", " \u2588\u2588 "],
498
+ W: ["\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588 \u2588\u2588", "\u2588\u2588\u2588 \u2588\u2588\u2588", "\u2588\u2588 \u2588\u2588"],
499
+ X: ["\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588"],
500
+ Y: ["\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 "],
501
+ Z: ["\u2588\u2588\u2588\u2588\u2588\u2588", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 ", "\u2588\u2588\u2588\u2588\u2588\u2588"],
502
+ // Numbers
503
+ "0": [" \u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588 "],
504
+ "1": [" \u2588\u2588 ", "\u2588\u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 ", "\u2588\u2588\u2588\u2588"],
505
+ "2": [" \u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", " \u2588\u2588 ", " \u2588\u2588 ", "\u2588\u2588\u2588\u2588\u2588\u2588"],
506
+ "3": ["\u2588\u2588\u2588\u2588\u2588 ", " \u2588\u2588", " \u2588\u2588\u2588\u2588 ", " \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588 "],
507
+ "4": ["\u2588\u2588 \u2588\u2588", "\u2588\u2588 \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588\u2588", " \u2588\u2588", " \u2588\u2588"],
508
+ "5": ["\u2588\u2588\u2588\u2588\u2588\u2588", "\u2588\u2588 ", "\u2588\u2588\u2588\u2588\u2588 ", " \u2588\u2588", "\u2588\u2588\u2588\u2588\u2588 "],
509
+ "6": [" \u2588\u2588\u2588\u2588 ", "\u2588\u2588 ", "\u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588 "],
510
+ "7": ["\u2588\u2588\u2588\u2588\u2588\u2588", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 "],
511
+ "8": [" \u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588 "],
512
+ "9": [" \u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588", " \u2588\u2588\u2588\u2588\u2588", " \u2588\u2588", " \u2588\u2588\u2588\u2588 "],
513
+ // Symbols
514
+ ".": [" ", " ", " ", " ", "\u2588\u2588"],
515
+ ",": [" ", " ", " ", "\u2588\u2588", "\u2588 "],
516
+ "!": ["\u2588\u2588", "\u2588\u2588", "\u2588\u2588", " ", "\u2588\u2588"],
517
+ "?": ["\u2588\u2588\u2588\u2588\u2588 ", " \u2588\u2588", " \u2588\u2588 ", " ", " \u2588\u2588 "],
518
+ "-": [" ", " ", "\u2588\u2588\u2588\u2588\u2588\u2588", " ", " "],
519
+ _: [" ", " ", " ", " ", "\u2588\u2588\u2588\u2588\u2588\u2588"],
520
+ ":": [" ", "\u2588\u2588", " ", "\u2588\u2588", " "],
521
+ "/": [" \u2588\u2588", " \u2588\u2588 ", " \u2588\u2588 ", " \u2588\u2588 ", "\u2588\u2588 "],
522
+ "@": [" \u2588\u2588\u2588\u2588\u2588 ", "\u2588\u2588 \u2588\u2588\u2588", "\u2588\u2588 \u2588\u2588\u2588\u2588", "\u2588\u2588 ", " \u2588\u2588\u2588\u2588\u2588 "],
523
+ "#": [" \u2588\u2588 \u2588\u2588 ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588", " \u2588\u2588 \u2588\u2588 ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588", " \u2588\u2588 \u2588\u2588 "],
524
+ " ": [" ", " ", " ", " ", " "]
525
+ };
526
+ var LINE_COUNT = 5;
527
+ function renderAsciiText(input) {
528
+ const chars = input.toUpperCase().split("");
529
+ const glyphs = chars.map((ch) => GLYPHS[ch] ?? GLYPHS[" "]);
530
+ const lines = [];
531
+ for (let row = 0; row < LINE_COUNT; row++) {
532
+ lines.push(glyphs.map((g) => g[row]).join(" "));
533
+ }
534
+ return lines.join("\n");
535
+ }
536
+ function renderAsciiLines(input) {
537
+ const chars = input.toUpperCase().split("");
538
+ const glyphs = chars.map((ch) => GLYPHS[ch] ?? GLYPHS[" "]);
539
+ const lines = [];
540
+ for (let row = 0; row < LINE_COUNT; row++) {
541
+ lines.push(glyphs.map((g) => g[row]).join(" "));
542
+ }
543
+ return lines;
544
+ }
454
545
  function Logo({ text, gradient = false, accent, className, style }) {
546
+ const lines = renderAsciiLines(text);
455
547
  const gradientValue = typeof gradient === "string" ? gradient : "var(--n3rd-gradient)";
456
- const logoStyle = {
548
+ const containerStyle = {
457
549
  fontFamily: "var(--n3rd-font)",
458
- fontSize: "var(--n3rd-text-2xl)",
550
+ fontSize: "var(--n3rd-text-sm)",
459
551
  fontWeight: 700,
460
552
  whiteSpace: "pre",
461
- lineHeight: 1.1,
462
- color: gradient ? "transparent" : accent ? `var(--n3rd-accent-${accent})` : "var(--n3rd-text-primary)",
463
- ...gradient && {
464
- background: gradientValue,
465
- backgroundClip: "text",
466
- WebkitBackgroundClip: "text",
467
- WebkitTextFillColor: "transparent"
468
- },
553
+ lineHeight: 1,
554
+ letterSpacing: "-0.05em",
555
+ color: accent ? `var(--n3rd-accent-${accent})` : "var(--n3rd-text-primary)",
469
556
  ...style
470
557
  };
471
- return /* @__PURE__ */ jsx("div", { className, style: logoStyle, "aria-label": text, role: "img", children: text });
558
+ const gradientStyle = gradient ? {
559
+ background: gradientValue,
560
+ backgroundClip: "text",
561
+ WebkitBackgroundClip: "text",
562
+ WebkitTextFillColor: "transparent"
563
+ } : {};
564
+ return /* @__PURE__ */ jsx("div", { className, style: containerStyle, "aria-label": text, role: "img", children: lines.map((line, i) => /* @__PURE__ */ jsx("div", { style: gradientStyle, children: line }, i)) });
472
565
  }
473
566
  function StatusLine({ left, center, right, className, style }) {
474
567
  const lineStyle = {
@@ -489,9 +582,6 @@ function StatusLine({ left, center, right, className, style }) {
489
582
  /* @__PURE__ */ jsx("div", { children: right })
490
583
  ] });
491
584
  }
492
-
493
- // src/components/display/Footer.module.css
494
- var Footer_default = {};
495
585
  function Footer({
496
586
  tagline = "where we're going, we don't need images",
497
587
  links = [],
@@ -499,47 +589,47 @@ function Footer({
499
589
  className,
500
590
  style
501
591
  }) {
502
- return /* @__PURE__ */ jsxs("footer", { className: `${Footer_default.footer} ${className ?? ""}`, style, children: [
503
- branding && /* @__PURE__ */ jsxs("div", { className: Footer_default.branding, children: [
592
+ return /* @__PURE__ */ jsxs("footer", { className: `n3rd-footer ${className ?? ""}`, style, children: [
593
+ branding && /* @__PURE__ */ jsxs("div", { className: "n3rd-footer-branding", children: [
504
594
  "\u2500\u2500 ",
505
595
  branding,
506
596
  " \u2500\u2500"
507
597
  ] }),
508
- /* @__PURE__ */ jsxs("div", { className: Footer_default.starfield, "aria-hidden": "true", children: [
509
- /* @__PURE__ */ jsx("div", { className: Footer_default.starRow, children: "\xB7 \u2726 \xB7 \xB7 \xB7 \u2726 \xB7" }),
510
- /* @__PURE__ */ jsx("div", { className: Footer_default.starRow, children: " \xB7 \u2726 \xB7 \xB7 \xB7 \xB7" }),
511
- /* @__PURE__ */ jsx("div", { className: Footer_default.starRow, children: "\u2726 \xB7 \xB7 \u2726 \xB7 \xB7" }),
512
- /* @__PURE__ */ jsx("div", { className: Footer_default.starRow, children: " \xB7 \xB7 \u2726 \xB7 \u2726" })
598
+ /* @__PURE__ */ jsxs("div", { className: "n3rd-footer-starfield", "aria-hidden": "true", children: [
599
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-star-row", children: "\xB7 \u2726 \xB7 \xB7 \xB7 \u2726 \xB7" }),
600
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-star-row", children: " \xB7 \u2726 \xB7 \xB7 \xB7 \xB7" }),
601
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-star-row", children: "\u2726 \xB7 \xB7 \u2726 \xB7 \xB7" }),
602
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-star-row", children: " \xB7 \xB7 \u2726 \xB7 \u2726" })
513
603
  ] }),
514
- /* @__PURE__ */ jsxs("div", { className: Footer_default.sunset, "aria-hidden": "true", children: [
515
- /* @__PURE__ */ jsx("div", { className: Footer_default.sunLine, style: { width: "30%", background: "#7c3aed" } }),
516
- /* @__PURE__ */ jsx("div", { className: Footer_default.sunLine, style: { width: "45%", background: "#a855f7" } }),
517
- /* @__PURE__ */ jsx("div", { className: Footer_default.sunGap }),
518
- /* @__PURE__ */ jsx("div", { className: Footer_default.sunLine, style: { width: "55%", background: "#c084fc" } }),
519
- /* @__PURE__ */ jsx("div", { className: Footer_default.sunLine, style: { width: "62%", background: "#ec4899" } }),
520
- /* @__PURE__ */ jsx("div", { className: Footer_default.sunGap }),
521
- /* @__PURE__ */ jsx("div", { className: Footer_default.sunLine, style: { width: "70%", background: "#f472b6" } }),
522
- /* @__PURE__ */ jsx("div", { className: Footer_default.sunLine, style: { width: "78%", background: "#fb7185" } }),
523
- /* @__PURE__ */ jsx("div", { className: Footer_default.sunGap, style: { height: "3px" } }),
524
- /* @__PURE__ */ jsx("div", { className: Footer_default.sunLine, style: { width: "85%", background: "#fda4af" } }),
525
- /* @__PURE__ */ jsx("div", { className: Footer_default.sunLine, style: { width: "92%", background: "#fecdd3" } })
604
+ /* @__PURE__ */ jsxs("div", { className: "n3rd-footer-sunset", "aria-hidden": "true", children: [
605
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "30%", background: "#7c3aed" } }),
606
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "45%", background: "#a855f7" } }),
607
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-gap" }),
608
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "55%", background: "#c084fc" } }),
609
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "62%", background: "#ec4899" } }),
610
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-gap" }),
611
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "70%", background: "#f472b6" } }),
612
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "78%", background: "#fb7185" } }),
613
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-gap", style: { height: "3px" } }),
614
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "85%", background: "#fda4af" } }),
615
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "92%", background: "#fecdd3" } })
526
616
  ] }),
527
- /* @__PURE__ */ jsxs("div", { className: Footer_default.horizon, "aria-hidden": "true", children: [
528
- /* @__PURE__ */ jsx("div", { className: Footer_default.horizonBright }),
529
- /* @__PURE__ */ jsx("div", { className: Footer_default.reflection }),
530
- /* @__PURE__ */ jsx("div", { className: Footer_default.reflectionDashed }),
531
- /* @__PURE__ */ jsx("div", { className: Footer_default.reflection, style: { opacity: 0.4 } }),
532
- /* @__PURE__ */ jsx("div", { className: Footer_default.reflectionDashed, style: { opacity: 0.2 } })
617
+ /* @__PURE__ */ jsxs("div", { className: "n3rd-footer-horizon", "aria-hidden": "true", children: [
618
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-horizon-bright" }),
619
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-reflection" }),
620
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-reflection-dashed" }),
621
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-reflection", style: { opacity: 0.4 } }),
622
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-reflection-dashed", style: { opacity: 0.2 } })
533
623
  ] }),
534
- tagline && /* @__PURE__ */ jsx("div", { className: Footer_default.tagline, children: tagline.toUpperCase() }),
535
- links.length > 0 && /* @__PURE__ */ jsx("nav", { className: Footer_default.links, children: links.map((link, i) => /* @__PURE__ */ jsxs("span", { children: [
536
- i > 0 && /* @__PURE__ */ jsx("span", { className: Footer_default.sep, children: " \u2502 " }),
624
+ tagline && /* @__PURE__ */ jsx("div", { className: "n3rd-footer-tagline", children: tagline.toUpperCase() }),
625
+ links.length > 0 && /* @__PURE__ */ jsx("nav", { className: "n3rd-footer-links", children: links.map((link, i) => /* @__PURE__ */ jsxs("span", { children: [
626
+ i > 0 && /* @__PURE__ */ jsx("span", { className: "n3rd-footer-sep", children: " \u2502 " }),
537
627
  /* @__PURE__ */ jsxs(
538
628
  "a",
539
629
  {
540
630
  href: link.href,
541
631
  ...link.external ? { target: "_blank", rel: "noopener noreferrer" } : {},
542
- className: Footer_default.link,
632
+ className: "n3rd-footer-link",
543
633
  children: [
544
634
  link.label,
545
635
  link.external ? " \u2197" : ""
@@ -549,10 +639,13 @@ function Footer({
549
639
  ] }, link.label)) })
550
640
  ] });
551
641
  }
552
-
553
- // src/components/input/Button.module.css
554
- var Button_default = {};
555
642
  var SPINNER_FRAMES = "\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F";
643
+ var VARIANT_CLASS = {
644
+ primary: "n3rd-btn-primary",
645
+ secondary: "n3rd-btn-secondary",
646
+ danger: "n3rd-btn-danger",
647
+ ghost: "n3rd-btn-ghost"
648
+ };
556
649
  function Button({
557
650
  children,
558
651
  variant = "primary",
@@ -565,16 +658,24 @@ function Button({
565
658
  style,
566
659
  type = "button"
567
660
  }) {
661
+ const [frame, setFrame] = useState(0);
662
+ useEffect(() => {
663
+ if (!loading) return;
664
+ const id = setInterval(() => {
665
+ setFrame((f) => (f + 1) % SPINNER_FRAMES.length);
666
+ }, 80);
667
+ return () => clearInterval(id);
668
+ }, [loading]);
568
669
  const classes = [
569
- Button_default.button,
570
- Button_default[variant],
571
- loading ? Button_default.loading : "",
572
- disabled ? Button_default.disabled : "",
670
+ "n3rd-btn",
671
+ VARIANT_CLASS[variant],
672
+ loading ? "n3rd-btn-loading" : "",
673
+ disabled ? "n3rd-btn-disabled" : "",
573
674
  className ?? ""
574
675
  ].filter(Boolean).join(" ");
575
676
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
576
677
  variant !== "ghost" && "[ ",
577
- loading && /* @__PURE__ */ jsx("span", { className: Button_default.spinner, "aria-hidden": "true", children: SPINNER_FRAMES[0] }),
678
+ loading && /* @__PURE__ */ jsx("span", { className: "n3rd-btn-spinner", "aria-hidden": "true", children: SPINNER_FRAMES[frame] }),
578
679
  loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
579
680
  children,
580
681
  "..."
@@ -620,9 +721,6 @@ function Cursor({ style = "block", className }) {
620
721
  };
621
722
  return /* @__PURE__ */ jsx("span", { className, style: cursorStyle, "aria-hidden": "true", children: CURSOR_CHARS[style] });
622
723
  }
623
-
624
- // src/components/input/Input.module.css
625
- var Input_default = {};
626
724
  function Input({
627
725
  label,
628
726
  placeholder,
@@ -653,41 +751,39 @@ function Input({
653
751
  onSubmit(value);
654
752
  }
655
753
  };
656
- return /* @__PURE__ */ jsxs("div", { className: `${Input_default.wrapper} ${className ?? ""}`, style, children: [
657
- label && /* @__PURE__ */ jsxs("label", { className: Input_default.label, htmlFor: id, children: [
754
+ const fieldClasses = [
755
+ "n3rd-input-field",
756
+ focused ? "n3rd-input-field-focused" : "",
757
+ disabled ? "n3rd-input-field-disabled" : ""
758
+ ].filter(Boolean).join(" ");
759
+ return /* @__PURE__ */ jsxs("div", { className: `n3rd-input-wrapper ${className ?? ""}`, style, children: [
760
+ label && /* @__PURE__ */ jsxs("label", { className: "n3rd-input-label", htmlFor: id, children: [
658
761
  label,
659
762
  ":"
660
763
  ] }),
661
- /* @__PURE__ */ jsxs(
662
- "div",
663
- {
664
- className: `${Input_default.field} ${focused ? Input_default.focused : ""} ${disabled ? Input_default.disabled : ""}`,
665
- onClick: () => inputRef.current?.focus(),
666
- children: [
667
- prefix && /* @__PURE__ */ jsx("span", { className: Input_default.prefix, children: prefix }),
668
- /* @__PURE__ */ jsxs("div", { className: Input_default.inputWrap, children: [
669
- /* @__PURE__ */ jsx(
670
- "input",
671
- {
672
- ref: inputRef,
673
- id,
674
- name,
675
- type,
676
- value,
677
- placeholder,
678
- onChange: handleChange,
679
- onKeyDown: handleKeyDown,
680
- onFocus: () => setFocused(true),
681
- onBlur: () => setFocused(false),
682
- disabled,
683
- className: Input_default.input
684
- }
685
- ),
686
- focused && !disabled && /* @__PURE__ */ jsx(Cursor, { style: cursor })
687
- ] })
688
- ]
689
- }
690
- )
764
+ /* @__PURE__ */ jsxs("div", { className: fieldClasses, onClick: () => inputRef.current?.focus(), children: [
765
+ prefix && /* @__PURE__ */ jsx("span", { className: "n3rd-input-prefix", children: prefix }),
766
+ /* @__PURE__ */ jsxs("div", { className: "n3rd-input-wrap", children: [
767
+ /* @__PURE__ */ jsx(
768
+ "input",
769
+ {
770
+ ref: inputRef,
771
+ id,
772
+ name,
773
+ type,
774
+ value,
775
+ placeholder,
776
+ onChange: handleChange,
777
+ onKeyDown: handleKeyDown,
778
+ onFocus: () => setFocused(true),
779
+ onBlur: () => setFocused(false),
780
+ disabled,
781
+ className: "n3rd-input"
782
+ }
783
+ ),
784
+ focused && !disabled && /* @__PURE__ */ jsx(Cursor, { style: cursor })
785
+ ] })
786
+ ] })
691
787
  ] });
692
788
  }
693
789
  var ICONS = {
@@ -774,18 +870,15 @@ function Skeleton({ width = 30, lines = 1, className, style }) {
774
870
  };
775
871
  return /* @__PURE__ */ jsx("div", { className, style: skeletonStyle, "aria-busy": "true", "aria-label": "Loading", children: Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx("div", { children: "\u2591".repeat(i === lines - 1 ? Math.ceil(width * 0.6) : width) }, i)) });
776
872
  }
777
-
778
- // src/components/nav/Nav.module.css
779
- var Nav_default = {};
780
873
  function Nav({ items, className, style }) {
781
- return /* @__PURE__ */ jsx("nav", { className: `${Nav_default.nav} ${className ?? ""}`, style, children: /* @__PURE__ */ jsx("div", { className: Nav_default.items, children: items.map((item) => /* @__PURE__ */ jsxs(
874
+ return /* @__PURE__ */ jsx("nav", { className: `n3rd-nav ${className ?? ""}`, style, children: /* @__PURE__ */ jsx("div", { className: "n3rd-nav-items", children: items.map((item) => /* @__PURE__ */ jsxs(
782
875
  "a",
783
876
  {
784
877
  href: item.href,
785
- className: `${Nav_default.item} ${item.active ? Nav_default.active : ""}`,
878
+ className: `n3rd-nav-item ${item.active ? "n3rd-nav-active" : ""}`,
786
879
  ...item.external ? { target: "_blank", rel: "noopener noreferrer" } : {},
787
880
  children: [
788
- item.active && /* @__PURE__ */ jsx("span", { className: Nav_default.indicator, children: "> " }),
881
+ item.active && /* @__PURE__ */ jsx("span", { className: "n3rd-nav-indicator", children: "> " }),
789
882
  "[ ",
790
883
  item.label,
791
884
  item.external ? " \u2197" : "",
@@ -810,8 +903,9 @@ function Typewriter({
810
903
  if (hasRun.current) return;
811
904
  hasRun.current = true;
812
905
  let index = 0;
906
+ let interval;
813
907
  const timeout = setTimeout(() => {
814
- const interval = setInterval(() => {
908
+ interval = setInterval(() => {
815
909
  if (index < text.length) {
816
910
  setDisplayed(text.slice(0, index + 1));
817
911
  index++;
@@ -821,9 +915,11 @@ function Typewriter({
821
915
  onComplete?.();
822
916
  }
823
917
  }, speed);
824
- return () => clearInterval(interval);
825
918
  }, delay);
826
- return () => clearTimeout(timeout);
919
+ return () => {
920
+ clearTimeout(timeout);
921
+ clearInterval(interval);
922
+ };
827
923
  }, [text, speed, delay, onComplete]);
828
924
  return /* @__PURE__ */ jsxs("span", { className, children: [
829
925
  displayed,
@@ -859,23 +955,14 @@ function N3rdProvider({
859
955
  scanlines && /* @__PURE__ */ jsx(Scanline, {})
860
956
  ] });
861
957
  }
862
- var jetbrainsMono = localFont({
863
- src: [
864
- {
865
- path: "../fonts/JetBrainsMono-Regular.woff2",
866
- weight: "400",
867
- style: "normal"
868
- },
869
- {
870
- path: "../fonts/JetBrainsMono-Bold.woff2",
871
- weight: "700",
872
- style: "normal"
873
- }
874
- ],
958
+
959
+ // src/theme/fonts.ts
960
+ var N3RD_FONT_FAMILY = "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', 'Consolas', 'Courier New', monospace";
961
+ var N3rdFonts = {
962
+ className: "",
875
963
  variable: "--n3rd-font",
876
- display: "swap",
877
- fallback: ["Fira Code", "Cascadia Code", "SF Mono", "Consolas", "Courier New", "monospace"]
878
- });
879
- var N3rdFonts = jetbrainsMono;
964
+ style: { fontFamily: N3RD_FONT_FAMILY }
965
+ };
966
+ var jetbrainsMono = N3rdFonts;
880
967
 
881
- export { Alert, Badge, Box, Button, Code, Cursor, Divider, Footer, Grid, Heading, Input, List, Logo, Metric, N3rdFonts, N3rdProvider, Nav, Page, Progress, Row, Scanline, Skeleton, Stack, StatusLine, Table, Text, Typewriter, jetbrainsMono };
968
+ export { Alert, Badge, Box, Button, Code, Cursor, Divider, Footer, Grid, Heading, Input, List, Logo, Metric, N3RD_FONT_FAMILY, N3rdFonts, N3rdProvider, Nav, Page, Progress, Row, Scanline, Skeleton, Stack, StatusLine, Table, Text, Typewriter, jetbrainsMono, renderAsciiLines, renderAsciiText };
@@ -0,0 +1,17 @@
1
+ @font-face {
2
+ font-family: 'JetBrains Mono';
3
+ font-style: normal;
4
+ font-weight: 400;
5
+ font-display: swap;
6
+ src: url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono@2.304/fonts/webfonts/JetBrainsMono-Regular.woff2')
7
+ format('woff2');
8
+ }
9
+
10
+ @font-face {
11
+ font-family: 'JetBrains Mono';
12
+ font-style: normal;
13
+ font-weight: 700;
14
+ font-display: swap;
15
+ src: url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono@2.304/fonts/webfonts/JetBrainsMono-Bold.woff2')
16
+ format('woff2');
17
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@n3rd-ai/ui",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Terminal-first UI framework for Next.js. ASCII everything. Zero images. Pure text.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -23,6 +23,8 @@
23
23
  "./theme/classic.css": "./dist/theme/presets/classic.css",
24
24
  "./theme/retro.css": "./dist/theme/presets/retro.css",
25
25
  "./theme/paper.css": "./dist/theme/presets/paper.css",
26
+ "./styles.css": "./dist/index.css",
27
+ "./theme/fonts.css": "./dist/theme/fonts.css",
26
28
  "./theme/tokens.css": "./dist/theme/tokens.css",
27
29
  "./theme/reset.css": "./dist/theme/reset.css"
28
30
  },
@@ -73,7 +75,10 @@
73
75
  "node": ">=20"
74
76
  },
75
77
  "sideEffects": [
76
- "**/*.css"
78
+ "**/*.css",
79
+ "./dist/index.js",
80
+ "./dist/chunk-*.js",
81
+ "./dist/hooks/index.js"
77
82
  ],
78
83
  "peerDependencies": {
79
84
  "next": ">=14.0.0",
@@ -1,21 +0,0 @@
1
- import localFont from 'next/font/local'
2
-
3
- export const jetbrainsMono = localFont({
4
- src: [
5
- {
6
- path: '../fonts/JetBrainsMono-Regular.woff2',
7
- weight: '400',
8
- style: 'normal',
9
- },
10
- {
11
- path: '../fonts/JetBrainsMono-Bold.woff2',
12
- weight: '700',
13
- style: 'normal',
14
- },
15
- ],
16
- variable: '--n3rd-font',
17
- display: 'swap',
18
- fallback: ['Fira Code', 'Cascadia Code', 'SF Mono', 'Consolas', 'Courier New', 'monospace'],
19
- })
20
-
21
- export const N3rdFonts = jetbrainsMono
File without changes
File without changes
File without changes
File without changes