create-table-app 0.0.1

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 ADDED
@@ -0,0 +1,29 @@
1
+ # create-table-app
2
+
3
+ Create a new Table.js Smart TV application in one command.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npx create-table-app my-tv-app
9
+ ```
10
+
11
+ Or with your preferred package manager:
12
+
13
+ ```bash
14
+ pnpm create table-app my-tv-app
15
+ yarn create table-app my-tv-app
16
+ bun create table-app my-tv-app
17
+ ```
18
+
19
+ ## What's Included
20
+
21
+ - File-based routing
22
+ - Focus management
23
+ - Tailwind CSS v4
24
+ - TypeScript
25
+ - Vite
26
+
27
+ ## License
28
+
29
+ MIT
package/dist/index.mjs ADDED
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import path2 from "path";
5
+ import chalk from "chalk";
6
+ import ora from "ora";
7
+ import { Command } from "commander";
8
+
9
+ // src/prompts.ts
10
+ import prompts from "prompts";
11
+ async function askProjectOptions(nameArg) {
12
+ const answers = await prompts([
13
+ {
14
+ type: nameArg ? null : "text",
15
+ name: "name",
16
+ message: "Project name",
17
+ initial: "my-table-app",
18
+ validate: (v) => {
19
+ return /^[a-z0-9-_]+$/.test(v) || "Use lowercase letters, numbers, - or _";
20
+ }
21
+ },
22
+ {
23
+ type: "select",
24
+ name: "platform",
25
+ message: "Target platform",
26
+ choices: [
27
+ { title: "All platforms", value: "all" },
28
+ { title: "Tizen (Samsung)", value: "tizen" },
29
+ { title: "WebOS (LG)", value: "webos" },
30
+ { title: "Android TV", value: "android" }
31
+ ],
32
+ initial: 0
33
+ },
34
+ {
35
+ type: "select",
36
+ name: "packageManager",
37
+ message: "Package manager",
38
+ choices: [
39
+ { title: "pnpm", value: "pnpm" },
40
+ { title: "npm", value: "npm" },
41
+ { title: "yarn", value: "yarn" },
42
+ { title: "bun", value: "bun" }
43
+ ],
44
+ initial: 0
45
+ },
46
+ {
47
+ type: "confirm",
48
+ name: "installDeps",
49
+ message: "Install dependencies?",
50
+ initial: true
51
+ },
52
+ {
53
+ type: "confirm",
54
+ name: "initGit",
55
+ message: "Initialize git repository?",
56
+ initial: true
57
+ }
58
+ ], {
59
+ onCancel: () => {
60
+ process.exit(0);
61
+ }
62
+ });
63
+ return {
64
+ name: nameArg ?? answers.name,
65
+ platform: answers.platform,
66
+ packageManager: answers.packageManager,
67
+ installDeps: answers.installDeps,
68
+ initGit: answers.initGit
69
+ };
70
+ }
71
+
72
+ // src/generator.ts
73
+ import path from "path";
74
+ import fs from "fs-extra";
75
+ import { execa } from "execa";
76
+ var TEMPLATE_DIR = path.join(
77
+ path.dirname(new URL(import.meta.url).pathname),
78
+ "..",
79
+ "templates",
80
+ "default"
81
+ );
82
+ async function applyTemplateVars(filePath, values) {
83
+ const TEXT_EXTENSIONS = [".ts", ".tsx", ".js", ".json", ".html", ".css", ".md"];
84
+ const ext = path.extname(filePath);
85
+ if (!TEXT_EXTENSIONS.includes(ext)) return;
86
+ let content = await fs.readFile(filePath, "utf-8");
87
+ let changed = false;
88
+ for (const [key, value] of Object.entries(values)) {
89
+ if (content.includes(key)) {
90
+ content = content.replaceAll(key, value);
91
+ changed = true;
92
+ }
93
+ }
94
+ if (changed) {
95
+ await fs.writeFile(filePath, content, "utf-8");
96
+ }
97
+ }
98
+ async function walk(dir, values) {
99
+ const entries = await fs.readdir(dir, { withFileTypes: true });
100
+ await Promise.all(
101
+ entries.map(async (entry) => {
102
+ const fullPath = path.join(dir, entry.name);
103
+ if (entry.isDirectory()) {
104
+ await walk(fullPath, values);
105
+ } else {
106
+ await applyTemplateVars(fullPath, values);
107
+ }
108
+ })
109
+ );
110
+ }
111
+ async function generateProject(targetDir, options) {
112
+ await fs.copy(TEMPLATE_DIR, targetDir);
113
+ const values = {
114
+ "{{name}}": options.name,
115
+ "{{platform}}": options.platform
116
+ };
117
+ await walk(targetDir, values);
118
+ }
119
+ async function installDeps(dir, pm) {
120
+ await execa(pm, ["install"], { cwd: dir, stdio: "inherit" });
121
+ }
122
+ async function initGit(dir) {
123
+ await execa("git", ["init"], { cwd: dir });
124
+ await execa("git", ["add", "-A"], { cwd: dir });
125
+ await execa("git", ["commit", "-m", "chore: initial commit from create-table-app"], { cwd: dir });
126
+ }
127
+
128
+ // src/index.ts
129
+ var program = new Command();
130
+ program.name("create-table-app").description("Create a new Table.js Smart TV application").version("0.0.1").argument("[project-name]", "Name of the project").action(async (projectName) => {
131
+ console.log();
132
+ console.log(chalk.bold(" table.js") + chalk.dim(" \u2014 Smart TV framework"));
133
+ console.log();
134
+ const options = await askProjectOptions(projectName);
135
+ const targetDir = path2.resolve(process.cwd(), options.name);
136
+ console.log();
137
+ const spinner = ora("Creating project structure").start();
138
+ try {
139
+ await generateProject(targetDir, options);
140
+ spinner.succeed("Project structure created");
141
+ } catch (err) {
142
+ spinner.fail("Failed to create project");
143
+ console.error(err);
144
+ process.exit(1);
145
+ }
146
+ if (options.installDeps) {
147
+ const depSpinner = ora("Installing dependencies").start();
148
+ try {
149
+ await installDeps(targetDir, options.packageManager);
150
+ depSpinner.succeed("Dependencies installed");
151
+ } catch {
152
+ depSpinner.fail("Failed to install dependencies");
153
+ }
154
+ }
155
+ if (options.initGit) {
156
+ const gitSpinner = ora("Initializing git").start();
157
+ try {
158
+ await initGit(targetDir);
159
+ gitSpinner.succeed("Git initialized");
160
+ } catch {
161
+ gitSpinner.fail("Failed to initialize git");
162
+ }
163
+ }
164
+ console.log();
165
+ console.log(chalk.green(" Project ready!"));
166
+ console.log();
167
+ console.log(" Next steps:");
168
+ console.log();
169
+ console.log(chalk.dim(` cd ${options.name}`));
170
+ if (!options.installDeps) {
171
+ console.log(chalk.dim(` ${options.packageManager} install`));
172
+ }
173
+ console.log(chalk.dim(` ${options.packageManager} run dev`));
174
+ console.log();
175
+ });
176
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "create-table-app",
3
+ "version": "0.0.1",
4
+ "description": "Create a new Table.js Smart TV app in one command",
5
+ "bin": {
6
+ "create-table-app": "./dist/index.mjs"
7
+ },
8
+ "files": [
9
+ "dist",
10
+ "templates"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup",
14
+ "dev": "tsup --watch",
15
+ "typecheck": "tsc --noEmit",
16
+ "clean": "rm -rf dist"
17
+ },
18
+ "keywords": [
19
+ "smart-tv",
20
+ "create-app",
21
+ "cli",
22
+ "tizen",
23
+ "webos",
24
+ "android-tv"
25
+ ],
26
+ "author": "Table.js Team",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/tablejs/tablejs"
31
+ },
32
+ "dependencies": {
33
+ "chalk": "^5.6.2",
34
+ "commander": "^14.0.3",
35
+ "execa": "^9.6.1",
36
+ "fs-extra": "^11.3.4",
37
+ "ora": "^9.3.0",
38
+ "prompts": "^2.4.2"
39
+ },
40
+ "devDependencies": {
41
+ "@table/tsconfig": "workspace:*",
42
+ "@types/fs-extra": "^11.0.4",
43
+ "@types/prompts": "^2.4.9",
44
+ "tsup": "^8.5.1",
45
+ "typescript": "^6.0.2"
46
+ }
47
+ }
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=1920">
6
+ <title>{{name}}</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "table build --platform {{platform}}",
9
+ "preview": "vite preview",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@table-js/core": "latest",
14
+ "@table-js/ui": "latest",
15
+ "react": "^19.2.4",
16
+ "react-dom": "^19.2.4"
17
+ },
18
+ "devDependencies": {
19
+ "@table-js/tsconfig": "latest",
20
+ "@types/react": "^19.2.4",
21
+ "@types/react-dom": "^19.2.4",
22
+ "@vitejs/plugin-react": "^6.0.1",
23
+ "@tailwindcss/vite": "^4.2.2",
24
+ "tailwindcss": "^4.2.2",
25
+ "typescript": "^6.0.2",
26
+ "vite": "^8.0.3"
27
+ }
28
+ }
@@ -0,0 +1,29 @@
1
+ import { Focusable, useParams, useRouter } from '@table/core'
2
+
3
+ interface MovieParams {
4
+ id: string
5
+ }
6
+
7
+ export default function MoviePage() {
8
+ const { id } = useParams<MovieParams>()
9
+ const { back } = useRouter()
10
+
11
+ return (
12
+ <div className="min-h-screen bg-(--color-background) text-(--color-text-primary) p-16">
13
+ <Focusable onSelect={back} autoFocus>
14
+ {(focused) => (
15
+ <button
16
+ className={`
17
+ px-6 py-3 rounded-(--radius-button) bg-(--color-surface) mb-8
18
+ transition-colors duration-150
19
+ ${focused ? 'bg-(--color-surface-hover) outline-3 outline-(--color-accent)' : ''}
20
+ `}
21
+ >
22
+ ← Back
23
+ </button>
24
+ )}
25
+ </Focusable>
26
+ <h1 className="text-5xl font-bold">Movie #{id}</h1>
27
+ </div>
28
+ )
29
+ }
@@ -0,0 +1,37 @@
1
+ import { Focusable, FocusGroup, useRouter } from '@table/core'
2
+
3
+ const movies = [
4
+ { id: '1', title: 'Inception' },
5
+ { id: '2', title: 'Interstellar' },
6
+ { id: '3', title: 'The Dark Knight' },
7
+ ]
8
+
9
+ export default function HomePage() {
10
+ const { navigate } = useRouter()
11
+
12
+ return (
13
+ <div className="min-h-screen bg-(--color-background) text-(--color-text-primary) p-16">
14
+ <h1 className="text-4xl font-bold mb-12">My TV App</h1>
15
+ <FocusGroup orientation="horizontal" loop>
16
+ {movies.map((movie) => (
17
+ <Focusable
18
+ key={movie.id}
19
+ onSelect={() => navigate(`/movie/${movie.id}`)}
20
+ >
21
+ {(focused) => (
22
+ <div
23
+ className={`
24
+ w-48 h-72 rounded-(--radius-card) bg-(--color-surface)
25
+ flex items-end p-4 mr-6 transition-transform duration-150
26
+ ${focused ? 'scale-105 outline-3 outline-(--color-accent)' : ''}
27
+ `}
28
+ >
29
+ <span className="font-semibold">{movie.title}</span>
30
+ </div>
31
+ )}
32
+ </Focusable>
33
+ ))}
34
+ </FocusGroup>
35
+ </div>
36
+ )
37
+ }
@@ -0,0 +1,21 @@
1
+ import { StrictMode } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import { TableApp, defineRoutes } from '@table/core'
4
+ import './styles/globals.css'
5
+
6
+ const routes = defineRoutes([
7
+ {
8
+ path: '/',
9
+ component: () => import('./app/page'),
10
+ },
11
+ {
12
+ path: '/movie/[id]',
13
+ component: () => import('./app/movie/[id]/page'),
14
+ },
15
+ ])
16
+
17
+ createRoot(document.getElementById('root')!).render(
18
+ <StrictMode>
19
+ <TableApp routes={routes} />
20
+ </StrictMode>,
21
+ )
@@ -0,0 +1,35 @@
1
+ @import "tailwindcss";
2
+
3
+ @theme {
4
+ --screen-tv: 1920px;
5
+
6
+ --color-background: #0a0a0a;
7
+ --color-surface: #18181b;
8
+ --color-surface-hover: #27272a;
9
+ --color-border: #3f3f46;
10
+ --color-text-primary: #fafafa;
11
+ --color-text-secondary: #a1a1aa;
12
+ --color-accent: #6366f1;
13
+ --color-accent-hover: #818cf8;
14
+
15
+ --radius-card: 0.75rem;
16
+ --radius-button: 0.5rem;
17
+
18
+ --font-sans: 'Inter', system-ui, sans-serif;
19
+ }
20
+
21
+ * {
22
+ box-sizing: border-box;
23
+ }
24
+
25
+ body {
26
+ background-color: var(--color-background);
27
+ color: var(--color-text-primary);
28
+ font-family: var(--font-sans);
29
+ overflow: hidden;
30
+ }
31
+
32
+ [data-focused="true"] {
33
+ outline: 3px solid var(--color-accent);
34
+ outline-offset: 3px;
35
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "@table/tsconfig/base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "target": "ES2022",
7
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
8
+ "moduleResolution": "bundler",
9
+ "allowImportingTsExtensions": true,
10
+ "noEmit": true
11
+ },
12
+ "include": ["src", "vite.config.ts"]
13
+ }
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import tailwindcss from '@tailwindcss/vite'
4
+
5
+ export default defineConfig({
6
+ plugins: [
7
+ react(),
8
+ tailwindcss()
9
+ ],
10
+ define: {
11
+ __TABLE_PLATFORM__: JSON.stringify("{{platform}}")
12
+ }
13
+ })