fabrica-e-commerce 0.1.17 → 0.2.2

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.
package/src/ui.js CHANGED
@@ -1,60 +1,50 @@
1
1
  import process from 'node:process';
2
2
  import boxen from 'boxen';
3
-
4
- // ── colors ────────────────────────────────────────────────────────────────────
5
- const ESC = '\x1b[';
6
- export const orange = (t) => `${ESC}38;2;255;138;0m${t}${ESC}0m`;
7
- export const dimOrange = (t) => `${ESC}38;2;182;95;0m${t}${ESC}0m`;
8
- export const bold = (t) => `${ESC}1m${t}${ESC}0m`;
9
- export const red = (t) => `${ESC}38;2;220;50;50m${t}${ESC}0m`;
10
- export const dim = (t) => `${ESC}2m${t}${ESC}0m`;
11
- export const green = (t) => `${ESC}38;2;80;200;80m${t}${ESC}0m`;
3
+ import chalk from 'chalk';
4
+ import gradient from 'gradient-string';
5
+ import figures from 'figures';
6
+
7
+ // ── palette ───────────────────────────────────────────────────────────────────
8
+ export const orange = (t) => chalk.hex('#ff8a00')(t);
9
+ export const dimOrange = (t) => chalk.hex('#b65f00')(t);
10
+ export const bold = (t) => chalk.bold(t);
11
+ export const red = (t) => chalk.hex('#dc3232')(t);
12
+ export const dim = (t) => chalk.dim(t);
13
+ export const green = (t) => chalk.hex('#50c850')(t);
14
+ export const cyan = (t) => chalk.hex('#00c8ff')(t);
15
+ export const yellow = (t) => chalk.hex('#ffc846')(t);
16
+ export const gray = (t) => chalk.hex('#888888')(t);
17
+ export const white = (t) => chalk.white(t);
12
18
 
13
19
  // strip ANSI for length calculations
14
20
  function stripAnsi(s) { return s.replace(/\x1b\[[0-9;]*m/g, ''); }
15
21
 
16
22
  // ── section buffer ─────────────────────────────────────────────────────────────
17
- // Each section accumulates lines, then flushes as ONE boxen box when the
18
- // next section (or flushSection) is called.
19
23
  let _currentTitle = null;
20
- let _lines = []; // raw strings (may contain ANSI)
24
+ let _lines = [];
21
25
  let _sectionCount = 0;
22
- let _isError = false;
23
26
 
24
27
  function flushSection() {
25
28
  if (_currentTitle === null) return;
26
-
27
29
  const content = _lines.join('\n');
28
30
  const rendered = boxen(content || ' ', {
29
- title: bold(orange(_currentTitle)),
31
+ title: ` ${bold(orange(_currentTitle))} `,
30
32
  titleAlignment: 'left',
31
- padding: { top: 0, bottom: 0, left: 1, right: 1 },
33
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
32
34
  margin: { top: 0, bottom: 0, left: 0, right: 0 },
33
35
  borderStyle: 'round',
34
36
  borderColor: '#ff8a00',
35
37
  });
36
-
37
- if (_sectionCount > 1) console.log(orange(' │'));
38
+ if (_sectionCount > 1) console.log(dimOrange(' │'));
38
39
  console.log(rendered);
39
-
40
40
  _currentTitle = null;
41
41
  _lines = [];
42
- _isError = false;
43
- }
44
-
45
- // Add a line into the current section buffer
46
- function addLine(text) {
47
- _lines.push(text);
48
42
  }
49
43
 
50
- export function resetSectionCount() {
51
- flushSection();
52
- _sectionCount = 0;
53
- }
44
+ function addLine(text) { _lines.push(text); }
54
45
 
55
- // ── public API ────────────────────────────────────────────────────────────────
46
+ export function resetSectionCount() { flushSection(); _sectionCount = 0; }
56
47
 
57
- // Start a new named section (flushes previous one)
58
48
  export function section(title) {
59
49
  flushSection();
60
50
  _sectionCount++;
@@ -62,49 +52,56 @@ export function section(title) {
62
52
  _lines = [];
63
53
  }
64
54
 
65
- // Force-flush the current section (call at end of a command)
66
- export function endSections() {
67
- flushSection();
68
- }
55
+ export function endSections() { flushSection(); }
69
56
 
70
- // kv row inside current section
71
57
  export function kv(key, value) {
72
- addLine(` ${dimOrange('›')} ${bold(key)} ${dimOrange('→')} ${value}`);
58
+ const k = bold(orange(key));
59
+ const arrow = dimOrange(figures.arrowRight);
60
+ const v = typeof value === 'string' && value.startsWith('http') ? cyan(value) : white(String(value));
61
+ addLine(` ${dimOrange(figures.pointer)} ${k} ${arrow} ${v}`);
62
+ }
63
+
64
+ export function kvSuccess(key, value) {
65
+ addLine(` ${green(figures.tick)} ${bold(key)} ${dimOrange(figures.arrowRight)} ${green(String(value))}`);
66
+ }
67
+
68
+ export function kvFail(key, value) {
69
+ addLine(` ${red(figures.cross)} ${bold(key)} ${dimOrange(figures.arrowRight)} ${red(String(value))}`);
73
70
  }
74
71
 
75
- // spinner — writes inline to stdout while running, then adds result line to buffer
72
+ // spinner — inline stdout, then adds result line to buffer
76
73
  const CLEAR_LINE = '\x1b[K';
77
74
  export function spinner(text) {
78
- process.stdout.write(` ${dimOrange('○')} ${text}`);
75
+ process.stdout.write(` ${dimOrange(figures.ellipsis)} ${dim(text)}`);
79
76
  return {
80
77
  succeed(msg) {
81
78
  process.stdout.write(`\r${CLEAR_LINE}`);
82
- addLine(` ${orange('✓')} ${msg}`);
79
+ addLine(` ${green(figures.tick)} ${msg}`);
83
80
  },
84
81
  fail(msg) {
85
82
  process.stdout.write(`\r${CLEAR_LINE}`);
86
- addLine(` ${red('✗')} ${msg}`);
83
+ addLine(` ${red(figures.cross)} ${msg}`);
87
84
  },
88
85
  };
89
86
  }
90
87
 
91
- // plain log inside current section
92
- export function log(text) {
93
- addLine(` ${text}`);
94
- }
88
+ export function log(text) { addLine(` ${dim(figures.line)} ${text}`); }
89
+ export function logInfo(text) { addLine(` ${cyan(figures.info)} ${text}`); }
90
+ export function logWarn(text) { addLine(` ${yellow(figures.warning)} ${yellow(text)}`); }
91
+
92
+ export function divider() { addLine(` ${dimOrange('─'.repeat(48))}`); }
95
93
 
96
- // sub-step block (e.g. raw Vercel output) — indented, dimmer border
94
+ // sub-step block
97
95
  export function subBox(lines, { isError = false } = {}) {
98
96
  if (!lines || !lines.length) return;
99
- const colored = isError ? lines.map((l) => red(l)) : lines.map((l) => dimOrange(l));
97
+ const colored = isError ? lines.map((l) => red(l)) : lines.map((l) => dim(l));
100
98
  const content = colored.join('\n');
101
99
  const rendered = boxen(content, {
102
- padding: { top: 0, bottom: 0, left: 1, right: 1 },
103
- margin: { top: 0, bottom: 0, left: 2, right: 0 },
100
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
101
+ margin: { top: 0, bottom: 0, left: 3, right: 0 },
104
102
  borderStyle: 'single',
105
103
  borderColor: isError ? '#dc3232' : '#b65f00',
106
104
  });
107
- // add each rendered line into buffer so it stays inside the section box
108
105
  for (const line of rendered.split('\n')) addLine(line);
109
106
  }
110
107
 
@@ -116,55 +113,85 @@ export function banner() {
116
113
  _lines = [];
117
114
 
118
115
  const art = [
119
- '███████╗ █████╗ ██████╗ ██████╗ ██╗ ██████╗ █████╗ ',
116
+ '███████╗ █████╗ ██████╗ ██████╗ ██╗ ██████╗ █████╗',
120
117
  '██╔════╝██╔══██╗██╔══██╗██╔══██╗██║██╔════╝██╔══██╗',
121
118
  '█████╗ ███████║██████╔╝██████╔╝██║██║ ███████║',
122
119
  '██╔══╝ ██╔══██║██╔══██╗██╔══██╗██║██║ ██╔══██║',
123
120
  '██║ ██║ ██║██████╔╝██║ ██║██║╚██████╗██║ ██║',
124
121
  '╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═════╝╚═╝ ╚═╝',
125
- ].map((l) => orange(l)).join('\n');
122
+ ];
123
+
124
+ const gradientArt = gradient(['#ff8a00', '#ff4500', '#ff8a00'])(art.join('\n'));
126
125
 
127
126
  const subtitle = [
128
- dimOrange('CMD OAUTH BRIDGE // SUPABASE + VERCEL DEPLOYER'),
129
- dim('by SPARROW AI SOLUTION'),
127
+ cyan(' Supabase + Vercel · Deploy in minutes'),
128
+ dim(' by SPARROW AI SOLUTION'),
130
129
  ].join('\n');
131
130
 
132
- console.log(boxen(`${art}\n\n${subtitle}`, {
133
- padding: { top: 0, bottom: 0, left: 1, right: 1 },
131
+ console.log(boxen(`${gradientArt}\n\n${subtitle}`, {
132
+ padding: { top: 1, bottom: 1, left: 3, right: 3 },
134
133
  borderStyle: 'double',
135
134
  borderColor: '#ff8a00',
135
+ textAlignment: 'center',
136
136
  }));
137
137
  }
138
138
 
139
139
  // ── help ──────────────────────────────────────────────────────────────────────
140
140
  export function help() {
141
141
  banner();
142
+
143
+ const commands = [
144
+ { cmd: 'build', desc: 'Connect Supabase · collect secrets · deploy or run locally' },
145
+ { cmd: 'list', desc: 'Show all Fabrica projects (local & cloud)' },
146
+ { cmd: 'env', desc: 'View and update environment variables for any project' },
147
+ { cmd: 'rerun', desc: 'Re-run or re-open an existing project' },
148
+ { cmd: 'vins', desc: 'Verify & auto-install CLI dependencies (git, gh, vercel)' },
149
+ { cmd: 'clean', desc: 'Remove local data, env files, or logout from Vercel / GitHub' },
150
+ { cmd: 'info', desc: 'Package, bridge, repo and storage info' },
151
+ { cmd: 'help', desc: 'Show this help screen' },
152
+ ];
153
+
154
+ const cmdRows = commands.map(({ cmd, desc }) =>
155
+ ` ${bold(orange(cmd.padEnd(10)))} ${dim(desc)}`
156
+ ).join('\n');
157
+
158
+ const examples = [
159
+ ` ${dimOrange('$')} npx fabrica-e-commerce build`,
160
+ ` ${dimOrange('$')} npx fabrica-e-commerce vins`,
161
+ ` ${dimOrange('$')} npx fabrica-e-commerce list`,
162
+ ` ${dimOrange('$')} npx fabrica-e-commerce clean`,
163
+ ` ${dimOrange('$')} npx fabrica-e-commerce env`,
164
+ ` ${dimOrange('$')} npx fabrica-e-commerce rerun`,
165
+ ].join('\n');
166
+
142
167
  const content = [
143
- bold(orange('Commands')),
168
+ bold(orange(' Commands')),
169
+ dimOrange(' ' + '─'.repeat(52)),
144
170
  '',
145
- ` ${bold('build')} Connect Supabase, collect secrets, deploy or run locally`,
146
- ` ${bold('list')} Show Fabrica projects (local & cloud)`,
147
- ` ${bold('env')} View and update environment variables for any project`,
148
- ` ${bold('rerun')} Re-run or re-open an existing project`,
149
- ` ${bold('vins')} Verify & auto-install CLI dependencies`,
150
- ` ${bold('info')} Package, bridge, repo and storage info`,
151
- ` ${bold('help')} Show this screen`,
171
+ cmdRows,
152
172
  '',
153
- bold(orange('Examples')),
173
+ bold(orange(' Examples')),
174
+ dimOrange(' ' + '─'.repeat(52)),
154
175
  '',
155
- ` ${dimOrange('$')} npx fabrica-e-commerce build`,
156
- ` ${dimOrange('$')} npx fabrica-e-commerce env`,
157
- ` ${dimOrange('$')} npx fabrica-e-commerce rerun`,
158
- ` ${dimOrange('$')} npx fabrica-e-commerce list`,
176
+ examples,
159
177
  '',
160
- dim('Creator: SPARROW AI SOLUTION'),
178
+ dim(` Creator: SPARROW AI SOLUTION · MIT License`),
161
179
  ].join('\n');
162
- console.log(orange(' │'));
180
+
181
+ console.log(dimOrange(' │'));
163
182
  console.log(boxen(content, {
164
- title: bold(orange('Help')),
183
+ title: ` ${bold(orange('Help & Commands'))} `,
165
184
  titleAlignment: 'left',
166
- padding: { top: 0, bottom: 0, left: 1, right: 1 },
185
+ padding: { top: 1, bottom: 1, left: 1, right: 1 },
167
186
  borderStyle: 'round',
168
187
  borderColor: '#ff8a00',
169
188
  }));
170
189
  }
190
+
191
+ // ── step progress ─────────────────────────────────────────────────────────────
192
+ export function stepHeader(step, total, title) {
193
+ endSections();
194
+ const badge = orange(`[${step}/${total}]`);
195
+ const line = `${badge} ${bold(white(title))}`;
196
+ console.log(`\n ${dimOrange(figures.arrowRight)} ${line}`);
197
+ }