bearnie 0.2.0 → 0.3.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.
- package/README.md +18 -1
- package/dist/index.js +83 -11
- package/dist/utils/config.d.ts +12 -0
- package/dist/utils/installed.d.ts +1 -1
- package/dist/utils/registry.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ This will:
|
|
|
63
63
|
- Install `clsx`, `tailwind-merge`, and `tailwindcss` dependencies
|
|
64
64
|
- Add the `@/*` path alias to `tsconfig.json`
|
|
65
65
|
- Wire `@tailwindcss/vite` into your Astro config (simple configs only — you get a hint otherwise)
|
|
66
|
-
-
|
|
66
|
+
- Ask for a base color and an accent color (see Themes below) and install the result to `src/styles/bearnie.css`
|
|
67
67
|
|
|
68
68
|
Projects created with `create-bearnie` already include `bearnie.json` and can skip init.
|
|
69
69
|
|
|
@@ -162,6 +162,23 @@ Any new npm dependencies the updated components need are installed automatically
|
|
|
162
162
|
- `-y, --yes` - Skip confirmation prompt
|
|
163
163
|
- `--cwd <path>` - Set the working directory
|
|
164
164
|
|
|
165
|
+
## Themes
|
|
166
|
+
|
|
167
|
+
Themes combine a **base color** (the grays used for backgrounds, text, and borders) with an **accent color** (buttons, focus rings, active states), both from Tailwind's official palette:
|
|
168
|
+
|
|
169
|
+
- **Bases:** `neutral` (default), `slate`, `gray`, `zinc`, `stone`, `mauve`, `olive`, `mist`, `taupe`
|
|
170
|
+
- **Accents:** neutral default, `red`, `rose`, `orange`, `amber`, `yellow`, `lime`, `green`, `emerald`, `teal`, `cyan`, `sky`, `blue`, `indigo`, `violet`, `purple`, `fuchsia`, `pink`
|
|
171
|
+
|
|
172
|
+
Every combination is a registry entry: `styles-blue` (neutral base, blue accent), `styles-slate` (slate base, neutral accent), `styles-slate-blue`, and so on. They all install the same `bearnie.css` file, so every component works with every theme.
|
|
173
|
+
|
|
174
|
+
`init` and `create-bearnie` ask for base and accent. Switch later with:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npx bearnie add styles-slate-blue --overwrite
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Switching records the theme in `bearnie.json`, so `diff` and `update` compare your CSS against the right palette.
|
|
181
|
+
|
|
165
182
|
## Package Managers
|
|
166
183
|
|
|
167
184
|
The CLI detects your package manager from the lockfile (`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `bun.lock`) and uses it for all dependency installs. No configuration needed.
|
package/dist/index.js
CHANGED
|
@@ -23,8 +23,20 @@ var DEFAULT_CONFIG = {
|
|
|
23
23
|
utilsDir: "src/utils",
|
|
24
24
|
stylesDir: "src/styles",
|
|
25
25
|
tailwindConfig: "tailwind.config.mjs",
|
|
26
|
-
typescript: true
|
|
26
|
+
typescript: true,
|
|
27
|
+
theme: "default"
|
|
27
28
|
};
|
|
29
|
+
function themeEntryName(theme) {
|
|
30
|
+
return theme === "default" ? "styles" : `styles-${theme}`;
|
|
31
|
+
}
|
|
32
|
+
function composeThemeName(base, accent) {
|
|
33
|
+
if (base === "neutral") return accent;
|
|
34
|
+
if (accent === "default") return base;
|
|
35
|
+
return `${base}-${accent}`;
|
|
36
|
+
}
|
|
37
|
+
function isThemeEntry(name) {
|
|
38
|
+
return name === "styles" || name.startsWith("styles-");
|
|
39
|
+
}
|
|
28
40
|
var CONFIG_FILE = "bearnie.json";
|
|
29
41
|
async function getProjectConfig(cwd) {
|
|
30
42
|
const configPath = path.join(cwd, CONFIG_FILE);
|
|
@@ -377,6 +389,14 @@ async function init(options) {
|
|
|
377
389
|
print.newline();
|
|
378
390
|
console.log(` ${chalk2.bold("Where should things go?")}`);
|
|
379
391
|
print.newline();
|
|
392
|
+
let themeBases = ["neutral"];
|
|
393
|
+
let themeAccents = ["default"];
|
|
394
|
+
try {
|
|
395
|
+
const index = await getRegistryIndex();
|
|
396
|
+
if (index.themeBases?.length) themeBases = index.themeBases;
|
|
397
|
+
if (index.themeAccents?.length) themeAccents = index.themeAccents;
|
|
398
|
+
} catch {
|
|
399
|
+
}
|
|
380
400
|
const responses = await prompts([
|
|
381
401
|
{
|
|
382
402
|
type: "text",
|
|
@@ -389,11 +409,37 @@ async function init(options) {
|
|
|
389
409
|
name: "utilsDir",
|
|
390
410
|
message: "Utilities directory",
|
|
391
411
|
initial: DEFAULT_CONFIG.utilsDir
|
|
392
|
-
}
|
|
412
|
+
},
|
|
413
|
+
...themeBases.length > 1 ? [
|
|
414
|
+
{
|
|
415
|
+
type: "select",
|
|
416
|
+
name: "themeBase",
|
|
417
|
+
message: "Base color (grays and surfaces)",
|
|
418
|
+
choices: themeBases.map((name) => ({
|
|
419
|
+
title: name,
|
|
420
|
+
value: name
|
|
421
|
+
})),
|
|
422
|
+
initial: 0
|
|
423
|
+
}
|
|
424
|
+
] : [],
|
|
425
|
+
...themeAccents.length > 1 ? [
|
|
426
|
+
{
|
|
427
|
+
type: "select",
|
|
428
|
+
name: "themeAccent",
|
|
429
|
+
message: "Accent color (buttons, focus rings)",
|
|
430
|
+
choices: themeAccents.map((name) => ({
|
|
431
|
+
title: name === "default" ? "default (neutral)" : name,
|
|
432
|
+
value: name
|
|
433
|
+
})),
|
|
434
|
+
initial: 0
|
|
435
|
+
}
|
|
436
|
+
] : []
|
|
393
437
|
]);
|
|
438
|
+
const { themeBase, themeAccent, ...dirs } = responses;
|
|
394
439
|
config = {
|
|
395
440
|
...config,
|
|
396
|
-
...
|
|
441
|
+
...dirs,
|
|
442
|
+
theme: composeThemeName(themeBase ?? "neutral", themeAccent ?? "default")
|
|
397
443
|
};
|
|
398
444
|
}
|
|
399
445
|
const spinner = ora({
|
|
@@ -469,7 +515,7 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
469
515
|
);
|
|
470
516
|
}
|
|
471
517
|
try {
|
|
472
|
-
const stylesEntry = await getComponent(
|
|
518
|
+
const stylesEntry = await getComponent(themeEntryName(config.theme));
|
|
473
519
|
let stylesWritten = false;
|
|
474
520
|
for (const file of stylesEntry.files) {
|
|
475
521
|
const stylesPath = path4.join(cwd, config.stylesDir, file.name);
|
|
@@ -480,12 +526,17 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
480
526
|
}
|
|
481
527
|
}
|
|
482
528
|
if (stylesWritten) {
|
|
529
|
+
const themeLabel = config.theme === "default" ? "" : ` (${config.theme} theme)`;
|
|
483
530
|
print.step(
|
|
484
|
-
`${brand.success("\u2713")} Added theme variables to ${chalk2.cyan(`${config.stylesDir}/bearnie.css`)}`
|
|
531
|
+
`${brand.success("\u2713")} Added theme variables to ${chalk2.cyan(`${config.stylesDir}/bearnie.css`)}${themeLabel}`
|
|
485
532
|
);
|
|
486
533
|
manualSteps.push(
|
|
487
534
|
`Import the styles in your global CSS: ${chalk2.cyan(`@import "./bearnie.css";`)} (after ${chalk2.cyan(`@import "tailwindcss";`)})`
|
|
488
535
|
);
|
|
536
|
+
} else if (config.theme !== "default") {
|
|
537
|
+
manualSteps.push(
|
|
538
|
+
`bearnie.css already exists \u2014 switch to the ${config.theme} theme with ${chalk2.cyan(`npx bearnie add ${themeEntryName(config.theme)} --overwrite`)}`
|
|
539
|
+
);
|
|
489
540
|
}
|
|
490
541
|
} catch {
|
|
491
542
|
manualSteps.push(`Add theme variables: ${chalk2.cyan("npx bearnie add styles")}`);
|
|
@@ -513,6 +564,7 @@ async function add(components, options) {
|
|
|
513
564
|
console.log(` ${messages.addStart()}`);
|
|
514
565
|
print.newline();
|
|
515
566
|
let config = await getProjectConfig(cwd);
|
|
567
|
+
const hadConfig = config !== null;
|
|
516
568
|
if (!config) {
|
|
517
569
|
print.warning(
|
|
518
570
|
`Project not initialized. Run ${chalk3.cyan("npx bearnie init")} first.`
|
|
@@ -566,8 +618,11 @@ async function add(components, options) {
|
|
|
566
618
|
}
|
|
567
619
|
selectedComponents = selected;
|
|
568
620
|
} else {
|
|
569
|
-
const availableNames = registryIndex.components.map((c) => c.name);
|
|
570
|
-
const
|
|
621
|
+
const availableNames = new Set(registryIndex.components.map((c) => c.name));
|
|
622
|
+
for (const theme of registryIndex.themes ?? []) {
|
|
623
|
+
availableNames.add(theme === "default" ? "styles" : `styles-${theme}`);
|
|
624
|
+
}
|
|
625
|
+
const invalid = components.filter((c) => !availableNames.has(c));
|
|
571
626
|
if (invalid.length > 0) {
|
|
572
627
|
print.error(messages.unknownComponent(invalid));
|
|
573
628
|
print.hint(`Run ${chalk3.cyan("npx bearnie list")} to see what's available.`);
|
|
@@ -672,6 +727,19 @@ async function add(components, options) {
|
|
|
672
727
|
print.hint(`${error}`);
|
|
673
728
|
}
|
|
674
729
|
}
|
|
730
|
+
if (hadConfig) {
|
|
731
|
+
const themeInstalled = selectedComponents.find(
|
|
732
|
+
(name) => isThemeEntry(name)
|
|
733
|
+
);
|
|
734
|
+
if (themeInstalled) {
|
|
735
|
+
const theme = themeInstalled === "styles" ? "default" : themeInstalled.replace(/^styles-/, "");
|
|
736
|
+
if (config.theme !== theme) {
|
|
737
|
+
config = { ...config, theme };
|
|
738
|
+
await saveProjectConfig(cwd, config);
|
|
739
|
+
print.step(`${brand.success("\u2713")} Theme set to ${chalk3.cyan(theme)} in bearnie.json`);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
675
743
|
const allDeps = [...npmDeps];
|
|
676
744
|
const allDevDeps = [...npmDevDeps];
|
|
677
745
|
if (allDeps.length > 0 || allDevDeps.length > 0) {
|
|
@@ -813,11 +881,14 @@ import { structuredPatch } from "diff";
|
|
|
813
881
|
import fs6 from "fs-extra";
|
|
814
882
|
async function getAllRegistryNames() {
|
|
815
883
|
const index = await getRegistryIndex();
|
|
816
|
-
const names = index.components.map((c) => c.name);
|
|
884
|
+
const names = new Set(index.components.map((c) => c.name));
|
|
817
885
|
for (const utility of index.utilities ?? []) {
|
|
818
|
-
names.
|
|
886
|
+
names.add(utility.name);
|
|
887
|
+
}
|
|
888
|
+
for (const theme of index.themes ?? []) {
|
|
889
|
+
names.add(themeEntryName(theme));
|
|
819
890
|
}
|
|
820
|
-
return names;
|
|
891
|
+
return [...names];
|
|
821
892
|
}
|
|
822
893
|
async function getEntryState(cwd, config, name) {
|
|
823
894
|
const entry = await getComponent(name);
|
|
@@ -856,7 +927,8 @@ async function getInstalledEntries(cwd, config, names) {
|
|
|
856
927
|
throw new Error(`Unknown components: ${unknown.join(", ")}`);
|
|
857
928
|
}
|
|
858
929
|
}
|
|
859
|
-
const
|
|
930
|
+
const activeTheme = themeEntryName(config.theme ?? "default");
|
|
931
|
+
const targets = names?.length ? names : allNames.filter((name) => !isThemeEntry(name) || name === activeTheme);
|
|
860
932
|
const installed = [];
|
|
861
933
|
for (const name of targets) {
|
|
862
934
|
const state = await getEntryState(cwd, config, name);
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -4,8 +4,20 @@ export interface ProjectConfig {
|
|
|
4
4
|
stylesDir: string;
|
|
5
5
|
tailwindConfig: string;
|
|
6
6
|
typescript: boolean;
|
|
7
|
+
/** Which color theme is installed ("default", "amber", ...). */
|
|
8
|
+
theme: string;
|
|
7
9
|
}
|
|
8
10
|
export declare const DEFAULT_CONFIG: ProjectConfig;
|
|
11
|
+
/** Registry entry name for a theme: "default" -> styles, "amber" -> styles-amber. */
|
|
12
|
+
export declare function themeEntryName(theme: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Theme name from a base (gray scale) + accent (primary color) pair:
|
|
15
|
+
* neutral+default -> "default", neutral+blue -> "blue",
|
|
16
|
+
* slate+default -> "slate", slate+blue -> "slate-blue".
|
|
17
|
+
*/
|
|
18
|
+
export declare function composeThemeName(base: string, accent: string): string;
|
|
19
|
+
/** True for the styles/styles-* entries, which all install the same CSS file. */
|
|
20
|
+
export declare function isThemeEntry(name: string): boolean;
|
|
9
21
|
export declare const CONFIG_FILE = "bearnie.json";
|
|
10
22
|
export declare function getProjectConfig(cwd: string): Promise<ProjectConfig | null>;
|
|
11
23
|
export declare function saveProjectConfig(cwd: string, config: ProjectConfig): Promise<void>;
|
|
@@ -17,7 +17,7 @@ export interface InstalledEntry {
|
|
|
17
17
|
files: FileState[];
|
|
18
18
|
hasChanges: boolean;
|
|
19
19
|
}
|
|
20
|
-
/** Every name in the registry: components, utilities, styles, barrel. */
|
|
20
|
+
/** Every name in the registry: components, utilities, styles, themes, barrel. */
|
|
21
21
|
export declare function getAllRegistryNames(): Promise<string[]>;
|
|
22
22
|
/**
|
|
23
23
|
* Compares one registry entry against the project. Returns null when the
|
package/dist/utils/registry.d.ts
CHANGED
|
@@ -28,6 +28,9 @@ export interface RegistryIndex {
|
|
|
28
28
|
name: string;
|
|
29
29
|
description: string;
|
|
30
30
|
}[];
|
|
31
|
+
themes?: string[];
|
|
32
|
+
themeBases?: string[];
|
|
33
|
+
themeAccents?: string[];
|
|
31
34
|
}
|
|
32
35
|
export declare function getRegistryIndex(): Promise<RegistryIndex>;
|
|
33
36
|
export declare function getComponent(name: string): Promise<RegistryComponent>;
|