Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ Full API reference with config interfaces, method signatures, and `@example` blo

```bash
thatopen create <name> [--template bim|default|cloud] # Scaffold project + auto npm install
# Use "." as name to scaffold in current directory
thatopen serve [--port N] # Dev server (esbuild watch + serve bundle)
thatopen login [--token T] [--api-url U] [--local] # Authenticate
thatopen publish [--name N] [--version-tag T] [--skip-build] [--app-id ID | --component-id ID]
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ npm run dev
# Open your project on platform.thatopen.com and click the debug button
```

You can also scaffold in the current directory:

```bash
mkdir my-app && cd my-app
npx thatopen create .
npm run dev
```

### Create a cloud component

```bash
Expand All @@ -30,6 +38,8 @@ npm run run # Build and test locally
| `default` | `npx thatopen create my-app --template default` | Minimal app showing platform context |
| `cloud` | `npx thatopen create my-component --template cloud` | Server-side Node.js component |

Use `npx thatopen create .` to scaffold in the current directory instead of creating a new one.

## What's in this package

- **Library** — `EngineServicesClient` for interacting with the That Open API (files, folders, apps, cloud components, executions, permissions)
Expand Down Expand Up @@ -70,7 +80,7 @@ const client = new EngineServicesClient(ctx.accessToken, ctx.apiUrl, { useBearer

| Command | Description |
|---------|-------------|
| `thatopen create <name> [--template bim\|default\|cloud]` | Scaffold a new project |
| `thatopen create <name> [--template bim\|default\|cloud]` | Scaffold a new project (use `.` for current directory) |
| `thatopen serve [--port N]` | Dev server (esbuild watch + serve bundle) |
| `thatopen login [--token T] [--local]` | Authenticate with the platform |
| `thatopen publish` | Build and publish to the platform |
Expand Down
8 changes: 8 additions & 0 deletions src/built-in/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ declare class _AppManager {
private _container;
/** Whether the app has been initialised via {@link init}. */
private _initialized;
private static readonly STORAGE_KEY;
/** Whether the sidebar buttons are collapsed (icon-only). */
private _sidebarCollapsed;
constructor(components: any);
/**
* The setup configuration that defines elements and layouts.
Expand Down Expand Up @@ -108,6 +111,11 @@ declare class _AppManager {
* Creates the sidebar element with layout-switching buttons.
*/
private _createSidebar;
/**
* Pauses renderer resize observers, runs a layout-changing callback,
* then resizes and force-renders on the next frame to avoid canvas blink.
*/
private _safeLayoutChange;
/**
* Highlights the active layout button in the sidebar.
*/
Expand Down
31 changes: 21 additions & 10 deletions src/cli/commands/create.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Command } from 'commander';
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { basename, join, resolve } from 'node:path';
import { execSync } from 'node:child_process';
import { getIndexHtml } from '../templates/index-html';
import { getMainTs } from '../templates/main-js';
Expand Down Expand Up @@ -38,7 +38,7 @@ function getContextMd(template: Template): string {
}

export const createCommand = new Command('create')
.argument('<project-name>', 'Name of the project to create')
.argument('<project-name>', 'Name of the project to create (use "." for current directory)')
.option('-t, --template <template>', `Template (${TEMPLATES.join(', ')})`, 'bim')
.description('Scaffold a new ThatOpen app or cloud component project')
.action(async (projectName: string, opts: { template: string }) => {
Expand All @@ -51,18 +51,27 @@ export const createCommand = new Command('create')

const isCloud = template === 'cloud';
const projectKind = isCloud ? 'cloud component' : 'app';
const useCurrentDir = projectName === '.';

const targetDir = resolve(process.cwd(), projectName);
const targetDir = useCurrentDir
? process.cwd()
: resolve(process.cwd(), projectName);

if (existsSync(targetDir)) {
const packageName = useCurrentDir
? basename(process.cwd())
: projectName;

if (!useCurrentDir && existsSync(targetDir)) {
console.error(`Directory "${projectName}" already exists.`);
process.exit(1);
}

console.log(`Creating ThatOpen ${projectKind} "${projectName}" (template: ${template})...`);
console.log(`Creating ThatOpen ${projectKind} "${packageName}" (template: ${template})...`);

mkdirSync(targetDir, { recursive: true });
mkdirSync(join(targetDir, 'src'));
if (!useCurrentDir) {
mkdirSync(targetDir, { recursive: true });
}
mkdirSync(join(targetDir, 'src'), { recursive: true });

// Cloud components don't need index.html
if (!isCloud) {
Expand All @@ -71,7 +80,7 @@ export const createCommand = new Command('create')

writeFileSync(join(targetDir, 'src', 'main.ts'), getMainSource(template));
writeFileSync(join(targetDir, 'vite.config.js'), getViteConfig(template));
writeFileSync(join(targetDir, 'package.json'), getPackageJson(projectName, template));
writeFileSync(join(targetDir, 'package.json'), getPackageJson(packageName, template));
writeFileSync(
join(targetDir, '.gitignore'),
'node_modules\ndist\n*.zip\n.thatopen\n',
Expand All @@ -97,10 +106,12 @@ export const createCommand = new Command('create')
}

console.log('');
console.log(` Created ./${projectName}`);
console.log(useCurrentDir ? ' Project ready!' : ` Created ./${projectName}`);
console.log('');
console.log(' Next steps:');
console.log(` cd ${projectName}`);
if (!useCurrentDir) {
console.log(` cd ${projectName}`);
}
if (isCloud) {
console.log(' npm run login -- --token <token> # Authenticate');
console.log(' npm run run # Test locally');
Expand Down