bank20baht-cli 0.5.0 → 0.6.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 CHANGED
@@ -72,7 +72,7 @@ Add a matching entry to `knip.ts` so dead-code checks cover it.
72
72
  Same flags as `new`, but scaffolds a complete stack:
73
73
 
74
74
  ```
75
- apps/web React 19 + Vite
75
+ apps/web React 19 + Vite on bank20baht-ui (tokens, <baht-*> elements, dark toggle)
76
76
  apps/api Elysia — exports `type App`, the Eden type anchor
77
77
  libs/shared/eden treaty<App> — the one typed HTTP client
78
78
  bahtui.json generator config (see below)
@@ -126,7 +126,7 @@ Field DSL — comma-separated `key:type[:modifier…]`:
126
126
  | Part | Values |
127
127
  | ---------- | -------------------------------------------------------------------------------------------------------------- |
128
128
  | `key` | `[a-z][a-z0-9_]*`; the label is derived (`due_date` → "Due Date") |
129
- | `type` | `text` `textarea` `markdown` `number` `email` `password` `dropdown` `multiselect` `checkbox` `radio` `toggle` `date` `file` `rating` `range` `reference` |
129
+ | `type` | `text` `textarea` `markdown` `number` `currency` `email` `password` `dropdown` `multiselect` `checkbox` `radio` `toggle` `date` `file` `rating` `range` `reference` `tags` |
130
130
  | modifiers | `required` · `options=a\|b\|c` · `min=N` · `max=N` · `step=N` · `ref=LOOKUP` · `flag=NAME` |
131
131
 
132
132
  Invalid DSL (unknown type, empty options, non-numeric `min`) fails before anything is
@@ -15,6 +15,7 @@ const FIELD_TYPES = new Set([
15
15
  'textarea',
16
16
  'markdown',
17
17
  'number',
18
+ 'currency',
18
19
  'email',
19
20
  'password',
20
21
  'dropdown',
@@ -27,6 +28,7 @@ const FIELD_TYPES = new Set([
27
28
  'rating',
28
29
  'range',
29
30
  'reference',
31
+ 'tags',
30
32
  ]);
31
33
  const KEY_PATTERN = /^[a-z][a-z0-9_]*$/;
32
34
  const titleCase = (key) => key
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bank20baht-cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "Scaffold bun workspaces (bahtui new) and DDD monorepos (React + Elysia + Eden, bahtui create) plus bahtui generators.",
6
6
  "license": "MIT",
@@ -13,7 +13,7 @@ bun run dev # web on :5173, api on :3001
13
13
  ## Layout (DDD taxonomy)
14
14
 
15
15
  ```
16
- apps/web React 19 + Vite
16
+ apps/web React 19 + Vite on bank20baht-ui (import each <baht-*> you use in src/main.tsx)
17
17
  apps/api Elysia — exports `type App`, the Eden type anchor
18
18
  libs/shared/eden treaty<App> — the ONE typed http client
19
19
  libs/domain/<d>/model (later) entities + form JSON, framework-free
@@ -4,6 +4,14 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>{{name}}</title>
7
+ <script>
8
+ // apply the saved <baht-theme-toggle> choice before paint (no flash)
9
+ try {
10
+ var t = localStorage.getItem('baht-theme');
11
+ if (t === 'light' || t === 'dark')
12
+ document.documentElement.setAttribute('data-baht-theme', t);
13
+ } catch (e) {}
14
+ </script>
7
15
  </head>
8
16
  <body>
9
17
  <div id="root"></div>
@@ -9,6 +9,7 @@
9
9
  },
10
10
  "dependencies": {
11
11
  "@{{name}}/eden": "workspace:*",
12
+ "bank20baht-ui": "^0.18.8",
12
13
  "react": "^19.1.0",
13
14
  "react-dom": "^19.1.0"
14
15
  },
@@ -1,20 +1,31 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import { api } from '@{{name}}/eden';
3
- import { describeHealth } from './health';
3
+ import { describeHealth, healthTone, type HealthTone } from './health';
4
+
5
+ type Status = { label: string; tone: HealthTone | 'neutral' };
4
6
 
5
7
  export default function App() {
6
- const [status, setStatus] = useState('checking api…');
8
+ const [status, setStatus] = useState<Status>({ label: 'checking api…', tone: 'neutral' });
7
9
 
8
10
  useEffect(() => {
9
- api.health.get().then(({ data, error }) => {
10
- setStatus(error ? `api unreachable (start it: bun run dev)` : describeHealth(data));
11
+ void api.health.get().then(({ data, error }) => {
12
+ setStatus(
13
+ error
14
+ ? { label: 'api unreachable (start it: bun run dev)', tone: 'danger' }
15
+ : { label: describeHealth(data), tone: healthTone(data) },
16
+ );
11
17
  });
12
18
  }, []);
13
19
 
14
20
  return (
15
- <main style={{ fontFamily: 'system-ui', padding: '2rem' }}>
16
- <h1>{{name}}</h1>
17
- <p>{status}</p>
21
+ <main className="app">
22
+ <header className="app-bar">
23
+ <h1>{{name}}</h1>
24
+ <baht-theme-toggle />
25
+ </header>
26
+ <baht-card heading="API" subtitle="apps/api via libs/shared/eden">
27
+ <baht-badge tone={status.tone} label={status.label} />
28
+ </baht-card>
18
29
  </main>
19
30
  );
20
31
  }
@@ -0,0 +1,27 @@
1
+ /* Page chrome only. Components style themselves from the --baht-* tokens. */
2
+ body {
3
+ margin: 0;
4
+ background: var(--baht-color-bg);
5
+ color: var(--baht-color-text);
6
+ font-family: var(--baht-font-family);
7
+ }
8
+
9
+ .app {
10
+ max-width: 720px;
11
+ margin: 0 auto;
12
+ padding: var(--baht-space-8) var(--baht-space-4);
13
+ display: grid;
14
+ gap: var(--baht-space-4);
15
+ }
16
+
17
+ .app-bar {
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: space-between;
21
+ }
22
+
23
+ .app-bar h1 {
24
+ margin: 0;
25
+ font-family: var(--baht-font-display);
26
+ font-size: 1.5rem;
27
+ }
@@ -1,14 +1,28 @@
1
1
  import { describe, expect, it } from 'bun:test';
2
- import { describeHealth } from './health';
2
+ import { describeHealth, healthTone } from './health';
3
+
4
+ const service = '{{name}}-api';
3
5
 
4
6
  describe('describeHealth', () => {
5
7
  it('formats a healthy response', () => {
6
- expect(describeHealth({ status: 'ok', service: '{{name}}-api' })).toBe(
7
- '{{name}}-api is ok',
8
- );
8
+ expect(describeHealth({ status: 'ok', service })).toBe(`${service} is ok`);
9
9
  });
10
10
 
11
11
  it('handles a missing response', () => {
12
12
  expect(describeHealth(null)).toBe('no response from api');
13
13
  });
14
14
  });
15
+
16
+ describe('healthTone', () => {
17
+ it('is success when the api says ok', () => {
18
+ expect(healthTone({ status: 'ok', service })).toBe('success');
19
+ });
20
+
21
+ it('is warning for any other status', () => {
22
+ expect(healthTone({ status: 'degraded', service })).toBe('warning');
23
+ });
24
+
25
+ it('is danger with no response', () => {
26
+ expect(healthTone(null)).toBe('danger');
27
+ });
28
+ });
@@ -1,6 +1,14 @@
1
1
  export type Health = { status: string; service: string } | null;
2
2
 
3
+ /** `<baht-badge>` status tones the health line uses. */
4
+ export type HealthTone = 'success' | 'danger' | 'warning';
5
+
3
6
  export function describeHealth(health: Health): string {
4
7
  if (!health) return 'no response from api';
5
8
  return `${health.service} is ${health.status}`;
6
9
  }
10
+
11
+ export function healthTone(health: Health): HealthTone {
12
+ if (!health) return 'danger';
13
+ return health.status === 'ok' ? 'success' : 'warning';
14
+ }
@@ -1,8 +1,19 @@
1
1
  import { StrictMode } from 'react';
2
2
  import { createRoot } from 'react-dom/client';
3
+ // Register only the <baht-*> elements this app renders (one subpath per tag).
4
+ // `import 'bank20baht-ui'` registers all of them, at ~4x the bundle.
5
+ import 'bank20baht-ui/components/badge';
6
+ import 'bank20baht-ui/components/card';
7
+ import 'bank20baht-ui/components/theme-toggle';
8
+ import 'bank20baht-ui/react'; // JSX typings for every <baht-*>
9
+ import 'bank20baht-ui/tokens.css';
10
+ import './app.css';
3
11
  import App from './App';
4
12
 
5
- createRoot(document.getElementById('root')!).render(
13
+ const root = document.getElementById('root');
14
+ if (!root) throw new Error('#root missing from index.html');
15
+
16
+ createRoot(root).render(
6
17
  <StrictMode>
7
18
  <App />
8
19
  </StrictMode>,
@@ -2,7 +2,10 @@
2
2
  "name": "{{name}}-monorepo",
3
3
  "private": true,
4
4
  "type": "module",
5
- "workspaces": ["apps/*", "libs/**"],
5
+ "workspaces": [
6
+ "apps/*",
7
+ "libs/**"
8
+ ],
6
9
  "scripts": {
7
10
  "dev": "bun run --filter './apps/*' dev",
8
11
  "build": "bun run --filter './apps/*' build",
@@ -16,10 +19,17 @@
16
19
  "prepare": "husky"
17
20
  },
18
21
  "lint-staged": {
19
- "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
20
- "*.{css,json,js,mjs}": ["prettier --write"]
22
+ "*.{ts,tsx}": [
23
+ "eslint --fix",
24
+ "prettier --write"
25
+ ],
26
+ "*.{css,json,js,mjs}": [
27
+ "prettier --write"
28
+ ]
21
29
  },
22
- "trustedDependencies": ["esbuild"],
30
+ "trustedDependencies": [
31
+ "esbuild"
32
+ ],
23
33
  "devDependencies": {
24
34
  "@eslint/js": "^10.0.1",
25
35
  "eslint": "^10.6.0",
@@ -33,7 +33,7 @@
33
33
  "@eslint/css": "^2.0.0",
34
34
  "@eslint/js": "^10.0.1",
35
35
  "@types/bun": "^1.3.0",
36
- "bank20baht-strict": "0.0.3",
36
+ "bank20baht-strict": "0.0.5",
37
37
  "dependency-cruiser": "^18.3.1",
38
38
  "eslint": "^10.10.0",
39
39
  "eslint-config-prettier": "^10.1.8",