create-buntui 0.0.0 → 0.1.0-alpha.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.
Files changed (40) hide show
  1. package/dist/buntui.so +0 -0
  2. package/dist/index.js +7 -0
  3. package/package.json +12 -5
  4. package/templates/basic/README.md +17 -0
  5. package/templates/basic/package.json +12 -2
  6. package/templates/basic/scripts/build.ts +65 -0
  7. package/templates/basic/src/App.vue +31 -0
  8. package/templates/basic/src/dev.ts +27 -0
  9. package/templates/basic/src/main.ts +15 -0
  10. package/templates/basic/tsconfig.json +28 -0
  11. package/templates/full/README.md +39 -0
  12. package/templates/full/package.json +21 -0
  13. package/templates/full/scripts/build.ts +66 -0
  14. package/templates/full/src/App.vue +49 -0
  15. package/templates/full/src/components/BoxDemo.vue +85 -0
  16. package/templates/full/src/components/ButtonDemo.vue +32 -0
  17. package/templates/full/src/components/CheckboxDemo.vue +19 -0
  18. package/templates/full/src/components/InputDemo.vue +36 -0
  19. package/templates/full/src/components/ProgressDemo.vue +25 -0
  20. package/templates/full/src/components/RadioDemo.vue +23 -0
  21. package/templates/full/src/components/ScrollBoxDemo.vue +55 -0
  22. package/templates/full/src/components/SelectDemo.vue +43 -0
  23. package/templates/full/src/components/SwitchDemo.vue +19 -0
  24. package/templates/full/src/components/TableDemo.vue +35 -0
  25. package/templates/full/src/components/TextDemo.vue +72 -0
  26. package/templates/full/src/components/TextareaDemo.vue +37 -0
  27. package/templates/full/src/dev.ts +40 -0
  28. package/templates/full/src/main.ts +21 -0
  29. package/templates/full/tsconfig.json +28 -0
  30. package/templates/sfc/README.md +28 -0
  31. package/templates/sfc/package.json +21 -0
  32. package/templates/sfc/scripts/build.ts +66 -0
  33. package/templates/sfc/src/App.vue +44 -0
  34. package/templates/sfc/src/dev.ts +40 -0
  35. package/templates/sfc/src/main.ts +21 -0
  36. package/templates/sfc/tsconfig.json +28 -0
  37. package/src/index.ts +0 -10
  38. package/src/scaffold.ts +0 -25
  39. package/src/setup-ui.ts +0 -145
  40. package/templates/basic/index.ts +0 -30
@@ -0,0 +1,28 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["ESNext"],
4
+ "target": "ESNext",
5
+ "module": "Preserve",
6
+ "moduleDetection": "force",
7
+ "moduleResolution": "bundler",
8
+ "allowImportingTsExtensions": true,
9
+ "verbatimModuleSyntax": true,
10
+ "noEmit": true,
11
+ "strict": true,
12
+ "skipLibCheck": true,
13
+ "noFallthroughCasesInSwitch": true,
14
+ "noUncheckedIndexedAccess": true,
15
+ "noImplicitOverride": true,
16
+ "erasableSyntaxOnly": true,
17
+ "types": ["bun"],
18
+ "vueCompilerOptions": {
19
+ "target": 3.5,
20
+ "strictTemplates": true
21
+ }
22
+ },
23
+ "include": [
24
+ "src/**/*.ts",
25
+ "src/**/*.vue",
26
+ "node_modules/@buntui/core/dist/global.d.ts"
27
+ ]
28
+ }
@@ -0,0 +1,28 @@
1
+ # {{name}}
2
+
3
+ A terminal UI application built with [buntui](https://github.com/AlphaFoxz/buntui) using SFC (Single File Component) templates.
4
+
5
+ ## Development
6
+
7
+ ```bash
8
+ bun run dev
9
+ ```
10
+
11
+ Hot module replacement (HMR) is enabled. Edit `.vue` files and see changes live.
12
+
13
+ ## Build
14
+
15
+ ```bash
16
+ bun run build
17
+ ```
18
+
19
+ The built output is in `dist/`.
20
+
21
+ ## Project Structure
22
+
23
+ ```
24
+ src/
25
+ main.ts Production entry
26
+ dev.ts Dev server with HMR
27
+ App.vue Root SFC component
28
+ ```
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "bun --preload @buntui/compiler/vue-plugin src/dev.ts",
7
+ "build": "bun run ./scripts/build.ts"
8
+ },
9
+ "dependencies": {
10
+ "@buntui/core": "{{version}}",
11
+ "@buntui/compiler": "{{version}}",
12
+ "@buntui/native": "{{version}}",
13
+ "@buntui/extensions": "{{version}}",
14
+ "@vue/reactivity": "^3.5.34"
15
+ },
16
+ "devDependencies": {
17
+ "@types/bun": "latest",
18
+ "vue": "^3.5.34",
19
+ "vue-tsc": "^3.2.8"
20
+ }
21
+ }
@@ -0,0 +1,66 @@
1
+ import process from 'node:process';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs';
4
+ import {compile, CORE_REGISTRY} from '@buntui/compiler';
5
+ import {getBinaryPath} from '@buntui/native';
6
+
7
+ const result = await Bun.build({
8
+ entrypoints: ['src/main.ts'],
9
+ outdir: 'dist',
10
+ target: 'bun',
11
+ minify: true,
12
+ plugins: [
13
+ {
14
+ name: 'buntui-vue',
15
+ setup(build) {
16
+ build.onLoad({filter: /\.vue$/v}, async args => {
17
+ const source = await Bun.file(args.path).text();
18
+ const compiled = compile(source, {
19
+ filename: args.path,
20
+ registry: CORE_REGISTRY,
21
+ codegen: {
22
+ coreModuleId: '@buntui/core',
23
+ reactivityModuleId: '@vue/reactivity',
24
+ },
25
+ });
26
+ return {contents: compiled.code, loader: 'ts'};
27
+ });
28
+ },
29
+ },
30
+ ],
31
+ });
32
+
33
+ if (!result.success) {
34
+ for (const error of result.logs) {
35
+ console.error(error);
36
+ }
37
+
38
+ process.exit(1);
39
+ }
40
+
41
+ for (const output of result.outputs) {
42
+ console.log(`Built: ${output.path} (${(output.size / 1024).toFixed(1)} KB)`);
43
+ }
44
+
45
+ function getBinaryExt(): string {
46
+ if (process.platform === 'win32') return 'dll';
47
+ if (process.platform === 'darwin') return 'dylib';
48
+ return 'so';
49
+ }
50
+
51
+ const binaryName = `buntui.${getBinaryExt()}`;
52
+ const nativePath = getBinaryPath();
53
+ const dllSearchPaths = [
54
+ ...(nativePath ? [nativePath] : []),
55
+ path.resolve(import.meta.dir, '..', 'node_modules', '@buntui', 'core', binaryName),
56
+ path.resolve(import.meta.dir, '..', binaryName),
57
+ ];
58
+
59
+ for (const candidate of dllSearchPaths) {
60
+ if (fs.existsSync(candidate)) {
61
+ const dest = path.resolve(import.meta.dir, '..', 'dist', binaryName);
62
+ fs.copyFileSync(candidate, dest);
63
+ console.log(`Copied: ${binaryName} -> dist/`);
64
+ break;
65
+ }
66
+ }
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <Box
3
+ :x="'20%'"
4
+ :y="'25%'"
5
+ :width="'60%'"
6
+ :height="12"
7
+ :colorBg="'rgba(30,30,46,1)'"
8
+ :borderColor="'rgba(137,180,250,1)'"
9
+ borderStyle="rounded"
10
+ :direction="'vertical'"
11
+ :gap="1"
12
+ :align="'center'"
13
+ :paddingTop="1"
14
+ :paddingBottom="1"
15
+ :paddingLeft="2"
16
+ :paddingRight="2"
17
+ >
18
+ <Text :colorFg="'rgba(137,180,250,1)'" :value="title" styleModifier="bold" />
19
+ <Text :colorFg="'rgba(166,227,161,1)'" value="SFC template with hot reload" />
20
+ <Text :colorFg="'rgba(205,214,244,1)'" value="Edit src/App.vue to get started" />
21
+ <Text :colorFg="'rgba(108,112,134,1)'" :value="clock" />
22
+ <Box :direction="'horizontal'" :gap="2" :align="'center'">
23
+ <Button :value="'Count: ' + count" @click="increment" />
24
+ </Box>
25
+ </Box>
26
+ </template>
27
+
28
+ <script setup lang="ts">
29
+ import { onTick } from '@buntui/core'
30
+ import { ref } from '@vue/reactivity'
31
+
32
+ const title = '{{name}}'
33
+ const clock = ref('')
34
+ const count = ref(0)
35
+
36
+ onTick(() => {
37
+ const now = new Date()
38
+ clock.value = now.toLocaleTimeString()
39
+ })
40
+
41
+ function increment() {
42
+ count.value++
43
+ }
44
+ </script>
@@ -0,0 +1,40 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+ import {createDevServer, CORE_REGISTRY, type DevServerOptions} from '@buntui/compiler';
4
+ import {mountHmrErrorOverlay, type HmrErrorOverlayHandle} from '@buntui/extensions/hmr-error-overlay';
5
+ import {ENTRY, run} from './main';
6
+
7
+ const DEV_DIR = path.join(import.meta.dir, '.dev');
8
+ fs.mkdirSync(DEV_DIR, {recursive: true});
9
+
10
+ const {scene} = run({logFilePath: DEV_DIR});
11
+
12
+ const VUE_FILE = path.join(import.meta.dir, ENTRY);
13
+
14
+ let errorOverlay: HmrErrorOverlayHandle | undefined;
15
+
16
+ createDevServer({
17
+ file: VUE_FILE,
18
+ tempDir: DEV_DIR,
19
+ compileOptions: {
20
+ registry: CORE_REGISTRY,
21
+ codegen: {
22
+ coreModuleId: '@buntui/core',
23
+ reactivityModuleId: '@vue/reactivity',
24
+ },
25
+ },
26
+ onClear() {
27
+ errorOverlay?.dismiss();
28
+ errorOverlay = undefined;
29
+ scene.clearWidgets();
30
+ },
31
+ onReload(setupFn: (scene: unknown) => void) {
32
+ errorOverlay?.dismiss();
33
+ errorOverlay = undefined;
34
+ setupFn(scene);
35
+ },
36
+ onError(error: Error) {
37
+ errorOverlay?.dismiss();
38
+ errorOverlay = mountHmrErrorOverlay(scene, error);
39
+ },
40
+ } satisfies DevServerOptions);
@@ -0,0 +1,21 @@
1
+ import {createApp} from '@buntui/core';
2
+ import App from './App.vue';
3
+
4
+ export const ENTRY = 'App.vue';
5
+
6
+ export function run(devOptions: {logFilePath?: string} = {}) {
7
+ const app = createApp({
8
+ logLevel: 'debug',
9
+ clearLog: true,
10
+ tickRate: 60,
11
+ renderRate: 24,
12
+ ...devOptions,
13
+ });
14
+ const scene = app.createScene(App, {bgHexRgb: 0x1A_1B_26, visible: true});
15
+ app.start();
16
+ return {app, scene};
17
+ }
18
+
19
+ if (import.meta.main) {
20
+ run();
21
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["ESNext"],
4
+ "target": "ESNext",
5
+ "module": "Preserve",
6
+ "moduleDetection": "force",
7
+ "moduleResolution": "bundler",
8
+ "allowImportingTsExtensions": true,
9
+ "verbatimModuleSyntax": true,
10
+ "noEmit": true,
11
+ "strict": true,
12
+ "skipLibCheck": true,
13
+ "noFallthroughCasesInSwitch": true,
14
+ "noUncheckedIndexedAccess": true,
15
+ "noImplicitOverride": true,
16
+ "erasableSyntaxOnly": true,
17
+ "types": ["bun"],
18
+ "vueCompilerOptions": {
19
+ "target": 3.5,
20
+ "strictTemplates": true
21
+ }
22
+ },
23
+ "include": [
24
+ "src/**/*.ts",
25
+ "src/**/*.vue",
26
+ "node_modules/@buntui/core/dist/global.d.ts"
27
+ ]
28
+ }
package/src/index.ts DELETED
@@ -1,10 +0,0 @@
1
- import process from 'node:process';
2
- import {createApp} from '@buntui/core';
3
- import {setupUI} from './setup-ui';
4
-
5
- const args = process.argv.slice(2);
6
- const defaultName = args[0];
7
-
8
- const app = createApp({logLevel: 'warning', clearLog: true});
9
- setupUI(app, defaultName);
10
- app.start();
package/src/scaffold.ts DELETED
@@ -1,25 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import {execSync} from 'node:child_process';
4
-
5
- const templateDir = path.resolve(import.meta.dir, '..', 'templates', 'basic');
6
-
7
- export function scaffold(projectName: string, targetDir: string): void {
8
- const outputDir = path.resolve(targetDir, projectName);
9
-
10
- if (fs.existsSync(outputDir)) {
11
- throw new Error(`Directory already exists: ${outputDir}`);
12
- }
13
-
14
- fs.mkdirSync(outputDir, {recursive: true});
15
-
16
- const files = fs.readdirSync(templateDir);
17
- for (const file of files) {
18
- const src = path.join(templateDir, file);
19
- const content = fs.readFileSync(src, 'utf-8');
20
- const rendered = content.replaceAll('{{name}}', projectName);
21
- fs.writeFileSync(path.join(outputDir, file), rendered, 'utf-8');
22
- }
23
-
24
- execSync('bun install', {cwd: outputDir, stdio: 'inherit'});
25
- }
package/src/setup-ui.ts DELETED
@@ -1,145 +0,0 @@
1
- #!/usr/bin/env bun
2
- import process from 'node:process';
3
- import {
4
- createBox,
5
- createTextWidget,
6
- createInputWidget,
7
- createButtonWidget,
8
- type createApp,
9
- } from '@buntui/core';
10
- import {scaffold} from './scaffold';
11
-
12
- type TuiApp = ReturnType<typeof createApp>;
13
-
14
- const ACCENT = 'rgb(122, 162, 247)';
15
- const BG = 'rgb(26, 27, 38)';
16
- const SURFACE = 'rgb(36, 40, 59)';
17
- const TEXT = 'rgb(192, 202, 229)';
18
- const TEXT_DIM = 'rgb(86, 95, 137)';
19
-
20
- export function setupUI(app: TuiApp, defaultName?: string): void {
21
- const scene = app.createScene({
22
- setup() {
23
- /* No-op */
24
- },
25
- }, {bgHexRgb: BG, visible: true});
26
-
27
- // Title bar
28
- scene.mount(createTextWidget({
29
- x: 0,
30
- y: 0,
31
- width: '100%',
32
- height: 1,
33
- value: ' Create Buntui App',
34
- colorFg: ACCENT,
35
- colorBg: BG,
36
- }));
37
-
38
- // Container box
39
- const box = createBox({
40
- x: '25%',
41
- y: '20%',
42
- width: '50%',
43
- height: 12,
44
- colorBg: SURFACE,
45
- borderColor: ACCENT,
46
- borderStyle: 'solid',
47
- });
48
- const input = createInputWidget({
49
- x: '0%',
50
- y: '22%',
51
- value: defaultName ?? 'my-buntui-app',
52
- colorFg: TEXT,
53
- colorBg: BG,
54
- borderColorUnfocused: TEXT_DIM,
55
- borderColorFocused: ACCENT,
56
- borderStyle: 'solid',
57
- placeholder: 'Type something...',
58
- label: 'Project name',
59
- });
60
- box.addChild(input);
61
- scene.mount(box);
62
-
63
- // Create button
64
- const createBtn = createButtonWidget({
65
- x: '35%',
66
- y: '50%',
67
- width: 12,
68
- height: 3,
69
- value: ' Create ',
70
- colorFgNormal: BG,
71
- colorBgNormal: ACCENT,
72
- borderColorNormal: ACCENT,
73
- borderStyleNormal: 'solid',
74
- colorFgFocused: BG,
75
- colorBgFocused: 'rgb(157, 122, 247)',
76
- borderColorFocused: 'rgb(157, 122, 247)',
77
- borderStyleFocused: 'solid',
78
- colorFgPressed: BG,
79
- colorBgPressed: 'rgb(91, 110, 181)',
80
- borderColorPressed: 'rgb(91, 110, 181)',
81
- borderStylePressed: 'solid',
82
- });
83
-
84
- // Cancel button
85
- const cancelBtn = createButtonWidget({
86
- x: '50%',
87
- y: '50%',
88
- width: 12,
89
- height: 3,
90
- value: ' Cancel ',
91
- colorFgNormal: TEXT_DIM,
92
- colorBgNormal: SURFACE,
93
- borderColorNormal: TEXT_DIM,
94
- borderStyleNormal: 'solid',
95
- colorFgFocused: TEXT,
96
- colorBgFocused: 'rgb(59, 66, 91)',
97
- borderColorFocused: TEXT,
98
- borderStyleFocused: 'solid',
99
- colorFgPressed: TEXT_DIM,
100
- colorBgPressed: 'rgb(42, 48, 69)',
101
- borderColorPressed: 'rgb(42, 48, 69)',
102
- borderStylePressed: 'solid',
103
- });
104
-
105
- // Status text
106
- const status = createTextWidget({
107
- x: '27%',
108
- y: '60%',
109
- width: '46%',
110
- height: 1,
111
- value: '',
112
- colorFg: TEXT_DIM,
113
- colorBg: BG,
114
- });
115
-
116
- createBtn.on('click', () => {
117
- const name = input.value.trim();
118
- if (!name) {
119
- status.updateValue(' Please enter a project name');
120
- return;
121
- }
122
-
123
- status.updateValue(' Creating project...');
124
- try {
125
- scaffold(name, process.cwd());
126
- status.updateValue(` Project "${name}" created successfully!`);
127
- setTimeout(() => {
128
- app.dispose();
129
- process.exit(0);
130
- }, 1500);
131
- } catch (error) {
132
- const message = error instanceof Error ? error.message : String(error);
133
- status.updateValue(` ${message}`);
134
- }
135
- });
136
-
137
- cancelBtn.on('click', () => {
138
- app.dispose();
139
- process.exit(0);
140
- });
141
-
142
- scene.mount(createBtn);
143
- scene.mount(cancelBtn);
144
- scene.mount(status);
145
- }
@@ -1,30 +0,0 @@
1
- import {createApp, createTextWidget, createBox} from 'buntui';
2
-
3
- const app = createApp();
4
- const scene = app.createScene({
5
- setup() {},
6
- }, {bgHexRgb: 0x1A_1B_26, visible: true});
7
-
8
- const title = createTextWidget({
9
- x: 0,
10
- y: 0,
11
- width: '100%',
12
- height: 1,
13
- value: 'Hello from {{name}}!',
14
- colorFg: 0x7A_A2_F7,
15
- colorBg: 0x1A_1B_26,
16
- });
17
-
18
- const box = createBox({
19
- x: '10%',
20
- y: '20%',
21
- width: '80%',
22
- height: '60%',
23
- colorBg: 0x24_28_3B,
24
- borderColor: 0x7A_A2_F7,
25
- borderStyle: 'solid',
26
- });
27
-
28
- scene.mount(box);
29
- scene.mount(title);
30
- app.start();