@n3rd-ai/ui 0.1.0 → 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.
@@ -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,97 @@ 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
553
  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
- },
554
+ color: accent ? `var(--n3rd-accent-${accent})` : "var(--n3rd-text-primary)",
469
555
  ...style
470
556
  };
471
- return /* @__PURE__ */ jsx("div", { className, style: logoStyle, "aria-label": text, role: "img", children: text });
557
+ const gradientStyle = gradient ? {
558
+ background: gradientValue,
559
+ backgroundClip: "text",
560
+ WebkitBackgroundClip: "text",
561
+ WebkitTextFillColor: "transparent"
562
+ } : {};
563
+ 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
564
  }
473
565
  function StatusLine({ left, center, right, className, style }) {
474
566
  const lineStyle = {
@@ -489,9 +581,6 @@ function StatusLine({ left, center, right, className, style }) {
489
581
  /* @__PURE__ */ jsx("div", { children: right })
490
582
  ] });
491
583
  }
492
-
493
- // src/components/display/Footer.module.css
494
- var Footer_default = {};
495
584
  function Footer({
496
585
  tagline = "where we're going, we don't need images",
497
586
  links = [],
@@ -499,47 +588,47 @@ function Footer({
499
588
  className,
500
589
  style
501
590
  }) {
502
- return /* @__PURE__ */ jsxs("footer", { className: `${Footer_default.footer} ${className ?? ""}`, style, children: [
503
- branding && /* @__PURE__ */ jsxs("div", { className: Footer_default.branding, children: [
591
+ return /* @__PURE__ */ jsxs("footer", { className: `n3rd-footer ${className ?? ""}`, style, children: [
592
+ branding && /* @__PURE__ */ jsxs("div", { className: "n3rd-footer-branding", children: [
504
593
  "\u2500\u2500 ",
505
594
  branding,
506
595
  " \u2500\u2500"
507
596
  ] }),
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" })
597
+ /* @__PURE__ */ jsxs("div", { className: "n3rd-footer-starfield", "aria-hidden": "true", children: [
598
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-star-row", children: "\xB7 \u2726 \xB7 \xB7 \xB7 \u2726 \xB7" }),
599
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-star-row", children: " \xB7 \u2726 \xB7 \xB7 \xB7 \xB7" }),
600
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-star-row", children: "\u2726 \xB7 \xB7 \u2726 \xB7 \xB7" }),
601
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-star-row", children: " \xB7 \xB7 \u2726 \xB7 \u2726" })
513
602
  ] }),
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" } })
603
+ /* @__PURE__ */ jsxs("div", { className: "n3rd-footer-sunset", "aria-hidden": "true", children: [
604
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "30%", background: "#7c3aed" } }),
605
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "45%", background: "#a855f7" } }),
606
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-gap" }),
607
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "55%", background: "#c084fc" } }),
608
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "62%", background: "#ec4899" } }),
609
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-gap" }),
610
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "70%", background: "#f472b6" } }),
611
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "78%", background: "#fb7185" } }),
612
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-gap", style: { height: "3px" } }),
613
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "85%", background: "#fda4af" } }),
614
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-sun-line", style: { width: "92%", background: "#fecdd3" } })
526
615
  ] }),
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 } })
616
+ /* @__PURE__ */ jsxs("div", { className: "n3rd-footer-horizon", "aria-hidden": "true", children: [
617
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-horizon-bright" }),
618
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-reflection" }),
619
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-reflection-dashed" }),
620
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-reflection", style: { opacity: 0.4 } }),
621
+ /* @__PURE__ */ jsx("div", { className: "n3rd-footer-reflection-dashed", style: { opacity: 0.2 } })
533
622
  ] }),
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 " }),
623
+ tagline && /* @__PURE__ */ jsx("div", { className: "n3rd-footer-tagline", children: tagline.toUpperCase() }),
624
+ links.length > 0 && /* @__PURE__ */ jsx("nav", { className: "n3rd-footer-links", children: links.map((link, i) => /* @__PURE__ */ jsxs("span", { children: [
625
+ i > 0 && /* @__PURE__ */ jsx("span", { className: "n3rd-footer-sep", children: " \u2502 " }),
537
626
  /* @__PURE__ */ jsxs(
538
627
  "a",
539
628
  {
540
629
  href: link.href,
541
630
  ...link.external ? { target: "_blank", rel: "noopener noreferrer" } : {},
542
- className: Footer_default.link,
631
+ className: "n3rd-footer-link",
543
632
  children: [
544
633
  link.label,
545
634
  link.external ? " \u2197" : ""
@@ -549,10 +638,13 @@ function Footer({
549
638
  ] }, link.label)) })
550
639
  ] });
551
640
  }
552
-
553
- // src/components/input/Button.module.css
554
- var Button_default = {};
555
641
  var SPINNER_FRAMES = "\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F";
642
+ var VARIANT_CLASS = {
643
+ primary: "n3rd-btn-primary",
644
+ secondary: "n3rd-btn-secondary",
645
+ danger: "n3rd-btn-danger",
646
+ ghost: "n3rd-btn-ghost"
647
+ };
556
648
  function Button({
557
649
  children,
558
650
  variant = "primary",
@@ -565,16 +657,24 @@ function Button({
565
657
  style,
566
658
  type = "button"
567
659
  }) {
660
+ const [frame, setFrame] = useState(0);
661
+ useEffect(() => {
662
+ if (!loading) return;
663
+ const id = setInterval(() => {
664
+ setFrame((f) => (f + 1) % SPINNER_FRAMES.length);
665
+ }, 80);
666
+ return () => clearInterval(id);
667
+ }, [loading]);
568
668
  const classes = [
569
- Button_default.button,
570
- Button_default[variant],
571
- loading ? Button_default.loading : "",
572
- disabled ? Button_default.disabled : "",
669
+ "n3rd-btn",
670
+ VARIANT_CLASS[variant],
671
+ loading ? "n3rd-btn-loading" : "",
672
+ disabled ? "n3rd-btn-disabled" : "",
573
673
  className ?? ""
574
674
  ].filter(Boolean).join(" ");
575
675
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
576
676
  variant !== "ghost" && "[ ",
577
- loading && /* @__PURE__ */ jsx("span", { className: Button_default.spinner, "aria-hidden": "true", children: SPINNER_FRAMES[0] }),
677
+ loading && /* @__PURE__ */ jsx("span", { className: "n3rd-btn-spinner", "aria-hidden": "true", children: SPINNER_FRAMES[frame] }),
578
678
  loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
579
679
  children,
580
680
  "..."
@@ -620,9 +720,6 @@ function Cursor({ style = "block", className }) {
620
720
  };
621
721
  return /* @__PURE__ */ jsx("span", { className, style: cursorStyle, "aria-hidden": "true", children: CURSOR_CHARS[style] });
622
722
  }
623
-
624
- // src/components/input/Input.module.css
625
- var Input_default = {};
626
723
  function Input({
627
724
  label,
628
725
  placeholder,
@@ -653,41 +750,39 @@ function Input({
653
750
  onSubmit(value);
654
751
  }
655
752
  };
656
- return /* @__PURE__ */ jsxs("div", { className: `${Input_default.wrapper} ${className ?? ""}`, style, children: [
657
- label && /* @__PURE__ */ jsxs("label", { className: Input_default.label, htmlFor: id, children: [
753
+ const fieldClasses = [
754
+ "n3rd-input-field",
755
+ focused ? "n3rd-input-field-focused" : "",
756
+ disabled ? "n3rd-input-field-disabled" : ""
757
+ ].filter(Boolean).join(" ");
758
+ return /* @__PURE__ */ jsxs("div", { className: `n3rd-input-wrapper ${className ?? ""}`, style, children: [
759
+ label && /* @__PURE__ */ jsxs("label", { className: "n3rd-input-label", htmlFor: id, children: [
658
760
  label,
659
761
  ":"
660
762
  ] }),
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
- )
763
+ /* @__PURE__ */ jsxs("div", { className: fieldClasses, onClick: () => inputRef.current?.focus(), children: [
764
+ prefix && /* @__PURE__ */ jsx("span", { className: "n3rd-input-prefix", children: prefix }),
765
+ /* @__PURE__ */ jsxs("div", { className: "n3rd-input-wrap", children: [
766
+ /* @__PURE__ */ jsx(
767
+ "input",
768
+ {
769
+ ref: inputRef,
770
+ id,
771
+ name,
772
+ type,
773
+ value,
774
+ placeholder,
775
+ onChange: handleChange,
776
+ onKeyDown: handleKeyDown,
777
+ onFocus: () => setFocused(true),
778
+ onBlur: () => setFocused(false),
779
+ disabled,
780
+ className: "n3rd-input"
781
+ }
782
+ ),
783
+ focused && !disabled && /* @__PURE__ */ jsx(Cursor, { style: cursor })
784
+ ] })
785
+ ] })
691
786
  ] });
692
787
  }
693
788
  var ICONS = {
@@ -774,18 +869,15 @@ function Skeleton({ width = 30, lines = 1, className, style }) {
774
869
  };
775
870
  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
871
  }
777
-
778
- // src/components/nav/Nav.module.css
779
- var Nav_default = {};
780
872
  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(
873
+ return /* @__PURE__ */ jsx("nav", { className: `n3rd-nav ${className ?? ""}`, style, children: /* @__PURE__ */ jsx("div", { className: "n3rd-nav-items", children: items.map((item) => /* @__PURE__ */ jsxs(
782
874
  "a",
783
875
  {
784
876
  href: item.href,
785
- className: `${Nav_default.item} ${item.active ? Nav_default.active : ""}`,
877
+ className: `n3rd-nav-item ${item.active ? "n3rd-nav-active" : ""}`,
786
878
  ...item.external ? { target: "_blank", rel: "noopener noreferrer" } : {},
787
879
  children: [
788
- item.active && /* @__PURE__ */ jsx("span", { className: Nav_default.indicator, children: "> " }),
880
+ item.active && /* @__PURE__ */ jsx("span", { className: "n3rd-nav-indicator", children: "> " }),
789
881
  "[ ",
790
882
  item.label,
791
883
  item.external ? " \u2197" : "",
@@ -810,8 +902,9 @@ function Typewriter({
810
902
  if (hasRun.current) return;
811
903
  hasRun.current = true;
812
904
  let index = 0;
905
+ let interval;
813
906
  const timeout = setTimeout(() => {
814
- const interval = setInterval(() => {
907
+ interval = setInterval(() => {
815
908
  if (index < text.length) {
816
909
  setDisplayed(text.slice(0, index + 1));
817
910
  index++;
@@ -821,9 +914,11 @@ function Typewriter({
821
914
  onComplete?.();
822
915
  }
823
916
  }, speed);
824
- return () => clearInterval(interval);
825
917
  }, delay);
826
- return () => clearTimeout(timeout);
918
+ return () => {
919
+ clearTimeout(timeout);
920
+ clearInterval(interval);
921
+ };
827
922
  }, [text, speed, delay, onComplete]);
828
923
  return /* @__PURE__ */ jsxs("span", { className, children: [
829
924
  displayed,
@@ -859,23 +954,14 @@ function N3rdProvider({
859
954
  scanlines && /* @__PURE__ */ jsx(Scanline, {})
860
955
  ] });
861
956
  }
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
- ],
957
+
958
+ // src/theme/fonts.ts
959
+ var N3RD_FONT_FAMILY = "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', 'Consolas', 'Courier New', monospace";
960
+ var N3rdFonts = {
961
+ className: "",
875
962
  variable: "--n3rd-font",
876
- display: "swap",
877
- fallback: ["Fira Code", "Cascadia Code", "SF Mono", "Consolas", "Courier New", "monospace"]
878
- });
879
- var N3rdFonts = jetbrainsMono;
963
+ style: { fontFamily: N3RD_FONT_FAMILY }
964
+ };
965
+ var jetbrainsMono = N3rdFonts;
880
966
 
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 };
967
+ 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.0",
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