dazscript-framework 0.3.2 → 1.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 +327 -267
- package/package.json +7 -4
- package/src/Setup.dsa.ts +37 -9
- package/src/dialog/builders/list-view-builder.ts +5 -0
- package/src/examples/01-hello-world.dsa.ts +8 -0
- package/src/examples/02-persistence-dialog.dsa.ts +21 -0
- package/src/examples/02-persistence-dialog.ts +68 -0
- package/src/examples/03-simple-dialog.dsa.ts +23 -0
- package/src/examples/03-simple-dialog.ts +47 -0
- package/src/examples/04-settings-dialog.dsa.ts +29 -0
- package/src/examples/04-settings-dialog.ts +83 -0
- package/src/examples/05-list-dialog.dsa.ts +53 -0
- package/src/examples/05-list-dialog.ts +88 -0
- package/src/examples/06-showcase-dialog.dsa.ts +87 -0
- package/src/examples/06-showcase-dialog.ts +518 -0
- package/src/helpers/custom-action-helper.ts +2 -1
- package/src/helpers/custom-action-installer-helper.ts +39 -10
- package/src/lib/observable.test.ts +416 -0
- package/src/lib/observable.ts +24 -18
- package/src/lib/tree-node.test.ts +21 -0
- package/tsconfig.json +28 -112
- package/webpack.config.js +1 -0
- package/src/samples/hello-world.dsa.ts +0 -8
- package/src/samples/sample-dialog.dsa.ts +0 -47
- package/src/samples/sample-dialog.ts +0 -49
- /package/src/{samples → examples}/config.ts +0 -0
package/README.md
CHANGED
|
@@ -1,441 +1,501 @@
|
|
|
1
1
|
# DazScript Framework
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**DazScript Framework** is a TypeScript toolkit for writing [Daz Studio](https://www.daz3d.com/daz-studio) scripts. It layers a full TypeScript development experience on top of [DAZ Script](https://docs.daz3d.com/public/software/dazstudio/4/referenceguide/scripting/start) (Qt Script / ECMAScript 5.1), and ships a fluent dialog builder so you can build UIs in code without touching the Qt widget API directly.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Why use it?
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
DAZ Script gives you direct access to the entire Daz Studio API. The DazScript Framework builds on that foundation and adds:
|
|
8
8
|
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
9
|
+
- **TypeScript everywhere** — full autocompletion and type checking for every Daz Studio API, your own models, and every helper in the framework.
|
|
10
|
+
- **Fast UI development** — a fluent builder API lets you describe dialogs declaratively without touching the Qt widget API by hand.
|
|
11
|
+
- **Two-way data binding** — link your data model to UI controls so they stay in sync automatically. The user types in a field and your model updates; you update the model in code and the UI reflects it instantly. No manual synchronization needed.
|
|
12
|
+
- **One-command build** — `npm run build` compiles TypeScript to `.dsa` files that Daz Studio runs directly.
|
|
13
|
+
- **Stable launcher shims** — built scripts use a two-level layout so iterating on your code never requires reinstalling actions in Daz Studio.
|
|
14
|
+
- **Automated installer generation** — `npm run installer` produces a full setup dialog by reading action metadata from your source code.
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
---
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
- A lightweight `action(...)` entrypoint plus helper methods for building interactive scripts.
|
|
17
|
-
- A generated setup dialog for installing, updating, and removing custom action registrations.
|
|
18
|
-
- Setup generation is fully automated from `action(...)` metadata, including menu path, toolbar, shortcut, description, grouping, icons, and bundle-based setup outputs.
|
|
19
|
-
- Easy integration with Daz Studio for quick script deployment.
|
|
18
|
+
## Quick Start: Hello World
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
A script that shows a message box in Daz Studio.
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
### 1. Install
|
|
24
23
|
|
|
25
24
|
```bash
|
|
26
25
|
npm install dazscript-framework dazscript-types
|
|
27
26
|
```
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
After installing the package, scaffold the project files:
|
|
28
|
+
### 2. Scaffold the project
|
|
32
29
|
|
|
33
30
|
```bash
|
|
34
31
|
npx dazscript init
|
|
35
32
|
```
|
|
36
33
|
|
|
37
|
-
|
|
34
|
+
Follow the prompt for your AppData author namespace (e.g. `YourName/my-project`). This creates `dazscript.config.ts`, `tsconfig.json`, and wires the `build`, `watch`, `icons`, and `installer` scripts into `package.json`.
|
|
38
35
|
|
|
39
|
-
|
|
36
|
+
### 3. Write the script
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
Create `src/hello-world.dsa.ts`:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { action } from '@dsf/core/action';
|
|
42
|
+
import { info } from '@dsf/helpers/message-box-helper';
|
|
43
|
+
|
|
44
|
+
action({ text: 'Hello World' }, () => {
|
|
45
|
+
info('Hello World!');
|
|
46
|
+
});
|
|
47
|
+
```
|
|
44
48
|
|
|
45
|
-
|
|
49
|
+
Files ending in `.dsa.ts` are compiled as runnable Daz Studio entry points.
|
|
46
50
|
|
|
47
|
-
|
|
51
|
+
### 4. Build
|
|
48
52
|
|
|
49
53
|
```bash
|
|
50
|
-
|
|
54
|
+
npm run build
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Output lands in `./out/`.
|
|
58
|
+
|
|
59
|
+
### 5. Load in Daz Studio
|
|
60
|
+
|
|
61
|
+
**Option A — Copy:** copy the `out/` folder into your Daz Studio scripts directory.
|
|
62
|
+
|
|
63
|
+
**Option B — Symlink** (recommended for development, re-runs pick up the latest build automatically):
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Windows — run as Administrator
|
|
67
|
+
mklink /D "C:\Users\[Username]\Documents\DAZ 3D\Studio\My Library\Scripts\MyScripts" "C:\path\to\project\out"
|
|
68
|
+
|
|
69
|
+
# macOS
|
|
70
|
+
ln -s /path/to/project/out ~/Documents/DAZ\ 3D/Studio/My\ Library/Scripts/MyScripts
|
|
51
71
|
```
|
|
52
72
|
|
|
53
|
-
|
|
54
|
-
- `--scripts-path` tells the installer generator where to scan for runnable `.dsa.ts` entry files.
|
|
55
|
-
- `--out-dir` sets the webpack build output directory for generated `.dsa` files and copied icons.
|
|
56
|
-
- `--app-data-path` sets the AppData namespace used by launcher fallback resolution. Use a unique `Author/Product` path.
|
|
73
|
+
Then in Daz Studio: **Scripts > MyScripts > Hello World**. A message box appears.
|
|
57
74
|
|
|
58
|
-
|
|
75
|
+
---
|
|
59
76
|
|
|
60
|
-
|
|
77
|
+
## Quick Start: A Simple Dialog
|
|
78
|
+
|
|
79
|
+
A dialog with a name input and an OK/Cancel button pair.
|
|
61
80
|
|
|
62
81
|
```typescript
|
|
63
|
-
import {
|
|
82
|
+
import { action } from '@dsf/core/action';
|
|
83
|
+
import { BasicDialog } from '@dsf/dialog/basic-dialog';
|
|
84
|
+
import { Observable } from '@dsf/lib/observable';
|
|
85
|
+
import { info } from '@dsf/helpers/message-box-helper';
|
|
64
86
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
87
|
+
// The model holds state
|
|
88
|
+
class GreetModel {
|
|
89
|
+
name$ = new Observable<string>('World');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// The dialog describes the UI
|
|
93
|
+
class GreetDialog extends BasicDialog {
|
|
94
|
+
constructor(private model: GreetModel) {
|
|
95
|
+
super('Greet');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
protected build(): void {
|
|
99
|
+
this.add.label('Enter your name:');
|
|
100
|
+
this.add.edit().value(this.model.name$);
|
|
101
|
+
this.add.button('Say Hello').clicked(() => this.dialog.accept());
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
action({ text: 'Greet Dialog' }, () => {
|
|
106
|
+
const model = new GreetModel();
|
|
107
|
+
const dialog = new GreetDialog(model);
|
|
108
|
+
|
|
109
|
+
if (dialog.ok()) {
|
|
110
|
+
info(`Hello, ${model.name$.value}!`);
|
|
111
|
+
}
|
|
71
112
|
});
|
|
72
113
|
```
|
|
73
114
|
|
|
74
|
-
`
|
|
115
|
+
`add.edit().value(observable)` creates a two-way binding: typing in the field updates `name$.value`, and assigning `name$.value` in code updates the field.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Documentation
|
|
75
120
|
|
|
76
|
-
|
|
121
|
+
### Installation & Setup
|
|
77
122
|
|
|
78
|
-
|
|
79
|
-
- `out/<folder>/lib/<script-name>/script.dsa` is the current implementation bundle that the launcher executes
|
|
123
|
+
Install the framework and its peer dependency:
|
|
80
124
|
|
|
81
|
-
|
|
125
|
+
```bash
|
|
126
|
+
npm install dazscript-framework dazscript-types
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Scaffold a new project:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npx dazscript init
|
|
133
|
+
```
|
|
82
134
|
|
|
83
|
-
|
|
135
|
+
If `--app-data-path` is not provided, `init` prompts for the AppData author namespace and uses the current folder name as the product segment.
|
|
84
136
|
|
|
85
|
-
|
|
137
|
+
This generates:
|
|
138
|
+
- `dazscript.config.ts`
|
|
139
|
+
- `tsconfig.json`
|
|
140
|
+
- `package.json` script wiring for `build`, `watch`, `icons`, and `installer`
|
|
86
141
|
|
|
87
|
-
|
|
142
|
+
Available `init` flags:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npx dazscript init --menu-path /MyScripts --scripts-path ./src --out-dir ./out --app-data-path YourName/my-project
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
| Flag | Description |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `--menu-path` | Default menu where scripts appear in Daz Studio |
|
|
151
|
+
| `--scripts-path` | Where the generator scans for runnable `.dsa.ts` entry files |
|
|
152
|
+
| `--out-dir` | Where `build` writes `.dsa` files and copies icons |
|
|
153
|
+
| `--app-data-path` | AppData namespace for launcher fallback (`Author/Product` format) |
|
|
154
|
+
|
|
155
|
+
Use `--scripts-path ./src/scripts` when runnable files live under a subfolder; use `--scripts-path ./src` when they are at the source root.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
### Project Configuration
|
|
160
|
+
|
|
161
|
+
`dazscript.config.ts` is the single configuration file for a project:
|
|
88
162
|
|
|
89
163
|
```typescript
|
|
90
|
-
import {
|
|
91
|
-
import { action } from '@dsf/core/action';
|
|
92
|
-
import { info } from '@dsf/helpers/message-box-helper';
|
|
164
|
+
import { defineConfig } from 'dazscript-framework/config';
|
|
93
165
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
166
|
+
export default defineConfig({
|
|
167
|
+
scriptsPath: './src',
|
|
168
|
+
outDir: './out',
|
|
169
|
+
defaultMenuPath: '/MyScripts',
|
|
170
|
+
appDataPath: 'YourName/my-project', // required
|
|
171
|
+
bundleName: 'My Project', // optional — used in the setup dialog title
|
|
97
172
|
});
|
|
98
173
|
```
|
|
99
174
|
|
|
175
|
+
`appDataPath` is required for builds that generate launcher shims and must be unique across your projects.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
100
179
|
### The `action(...)` Entrypoint
|
|
101
180
|
|
|
102
|
-
|
|
181
|
+
Every runnable `.dsa.ts` file calls `action(...)` at module scope. This defines how the script registers in Daz Studio and what it executes.
|
|
103
182
|
|
|
104
183
|
```typescript
|
|
105
184
|
action({
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
185
|
+
text: 'My Script',
|
|
186
|
+
menuPath: '#{defaultMenuPath}/Tools',
|
|
187
|
+
shortcut: 'CTRL+SHIFT+M',
|
|
188
|
+
toolbar: 'MyToolbar',
|
|
189
|
+
group: 'Tools',
|
|
190
|
+
description: 'Does something useful',
|
|
112
191
|
}, () => {
|
|
113
|
-
info('
|
|
192
|
+
info('Running!');
|
|
114
193
|
});
|
|
115
194
|
```
|
|
116
195
|
|
|
117
|
-
`action(...)` also accepts a
|
|
196
|
+
`action(...)` also accepts a class with a `run()` method:
|
|
118
197
|
|
|
119
198
|
```typescript
|
|
120
|
-
class
|
|
199
|
+
class MyScript {
|
|
121
200
|
run(): void {
|
|
122
|
-
info('
|
|
201
|
+
info('Running!');
|
|
123
202
|
}
|
|
124
203
|
}
|
|
125
204
|
|
|
126
|
-
action({ text: '
|
|
205
|
+
action({ text: 'My Script' }, MyScript);
|
|
127
206
|
```
|
|
128
207
|
|
|
129
|
-
|
|
208
|
+
| Parameter | Description |
|
|
209
|
+
|---|---|
|
|
210
|
+
| `text` | Label shown in Daz Studio |
|
|
211
|
+
| `menuPath` | Menu path where the action is registered. Set to `false` to skip. Defaults to `defaultMenuPath` from config. |
|
|
212
|
+
| `shortcut` | Keyboard shortcut (e.g. `CTRL+SHIFT+H`) |
|
|
213
|
+
| `toolbar` | Toolbar name the action should appear on |
|
|
214
|
+
| `group` | Grouping label for related actions in Daz Studio |
|
|
215
|
+
| `description` | Longer description for the action |
|
|
216
|
+
| `bundle` | Generates a setup script beside the action. `true` → `Setup.dsa.ts`, a string → `Setup <name>.dsa.ts` |
|
|
130
217
|
|
|
131
|
-
|
|
132
|
-
- `menuPath`: the menu path where the script should be added. Set it to `false` to skip adding the script to a menu. If omitted, the default menu from `--menu-path` is used.
|
|
133
|
-
- `shortcut`: the keyboard shortcut for the action.
|
|
134
|
-
- `toolbar`: the toolbar name used when the action should appear on a toolbar.
|
|
135
|
-
- `group`: an optional grouping label used by Daz Studio for related actions.
|
|
136
|
-
- `description`: a longer description for the action.
|
|
137
|
-
- `bundle`: generates an additional setup script next to the action file. Use `true` for `Setup.dsa.ts` or a string for `Setup <bundle>.dsa.ts`.
|
|
218
|
+
---
|
|
138
219
|
|
|
139
|
-
|
|
220
|
+
### Build Output: Launcher Shims
|
|
140
221
|
|
|
141
|
-
|
|
142
|
-
- the implementation bundle under a sibling `lib/<script-name>/script.dsa` path
|
|
222
|
+
Each built action produces two files:
|
|
143
223
|
|
|
144
|
-
|
|
224
|
+
- `out/<script>.dsa` — the stable **launcher** registered with Daz Studio (menus, toolbars, shortcuts)
|
|
225
|
+
- `out/<folder>/lib/<script-name>/script.dsa` — the **implementation bundle** the launcher executes
|
|
145
226
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
### Generated Setup Script
|
|
227
|
+
When you rebuild, only the implementation bundle changes. The launcher path stays stable, so re-registering the action in Daz Studio is normally not required.
|
|
149
228
|
|
|
150
|
-
|
|
229
|
+
At runtime the launcher looks for the local `lib/` bundle first, then falls back to `App.getAppDataPath()/<appDataPath>`.
|
|
151
230
|
|
|
152
|
-
|
|
231
|
+
---
|
|
153
232
|
|
|
154
|
-
|
|
233
|
+
### Generated Setup Script
|
|
155
234
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
- Derives action labels, descriptions, shortcuts, toolbar targets, grouping, and icons from the action definition
|
|
160
|
-
- Writes one searchable setup entry per discovered action
|
|
161
|
-
- Uses `appDataPath/Installer` as the installer settings namespace
|
|
162
|
-
- Passes `bundleName` through so the dialog title can be project-specific
|
|
235
|
+
```bash
|
|
236
|
+
npm run installer
|
|
237
|
+
```
|
|
163
238
|
|
|
164
|
-
|
|
239
|
+
This scans all `.dsa.ts` entry files, reads each top-level `action(...)` call, and generates `src/Setup.dsa.ts` automatically. No installer code to maintain by hand.
|
|
165
240
|
|
|
166
|
-
|
|
241
|
+
The generated setup dialog:
|
|
167
242
|
|
|
168
|
-
- Shows an install checkbox
|
|
169
|
-
- Includes a search box that filters
|
|
170
|
-
- Supports
|
|
171
|
-
- Lets the user right-click
|
|
172
|
-
-
|
|
173
|
-
-
|
|
174
|
-
- Uses the configured `bundleName` in the window title when available
|
|
243
|
+
- Shows an install checkbox per action with columns for Action, Shortcut, Description, Menu, and Toolbar
|
|
244
|
+
- Includes a search box that filters across all columns
|
|
245
|
+
- Supports Select All / Deselect All on the visible rows
|
|
246
|
+
- Lets the user right-click to set or reset a shortcut (overrides shown with `[ovr]`)
|
|
247
|
+
- Initializes from the current Daz Studio install state — already-installed actions show as checked
|
|
248
|
+
- Uses `bundleName` from `dazscript.config.ts` in the window title
|
|
175
249
|
|
|
176
|
-
Applying the
|
|
250
|
+
Applying the dialog:
|
|
251
|
+
- Checked rows are installed or updated
|
|
252
|
+
- Unchecked rows are removed from their menu and toolbar targets
|
|
253
|
+
- Affected toolbars are rebuilt; empty framework-created toolbars are removed
|
|
177
254
|
|
|
178
|
-
|
|
179
|
-
- Unselected rows are removed from supported menu and toolbar targets
|
|
180
|
-
- Affected toolbars are rebuilt after removal so remaining selected actions stay grouped correctly
|
|
181
|
-
- Empty toolbars created by the framework are cleaned up automatically
|
|
255
|
+
This replaces the older `Install.dsa.ts` / `Uninstall.dsa.ts` pattern. The installer generator removes those legacy files if they exist.
|
|
182
256
|
|
|
183
|
-
|
|
257
|
+
---
|
|
184
258
|
|
|
185
259
|
### Action-Level Bundles
|
|
186
260
|
|
|
187
|
-
The `bundle` property on `action(...)` is separate from project `bundleName
|
|
261
|
+
The `bundle` property on `action(...)` is separate from the project-level `bundleName` in config.
|
|
188
262
|
|
|
189
|
-
|
|
190
|
-
- `bundle` in an action definition changes installer generation behavior for that action
|
|
263
|
+
When `bundle` is set, the installer generator also writes a setup script beside that action:
|
|
191
264
|
|
|
192
|
-
|
|
265
|
+
- `bundle: true` → writes `Setup.dsa.ts`
|
|
266
|
+
- `bundle: 'Utilities'` → writes `Setup Utilities.dsa.ts`
|
|
193
267
|
|
|
194
|
-
|
|
195
|
-
- `bundle: 'Utilities'` writes `Setup Utilities.dsa.ts`
|
|
268
|
+
Those bundle-scoped setup files use the same setup dialog helper and also receive the project `bundleName`.
|
|
196
269
|
|
|
197
|
-
|
|
270
|
+
---
|
|
198
271
|
|
|
199
|
-
### Building UIs
|
|
272
|
+
### Building UIs: Dialogs & Observables
|
|
200
273
|
|
|
201
|
-
The framework uses a **Model-View pattern** with reactive
|
|
274
|
+
The framework uses a **Model-View pattern** with reactive bindings.
|
|
202
275
|
|
|
203
|
-
#### 1. Define
|
|
276
|
+
#### 1. Define a model
|
|
204
277
|
|
|
205
278
|
```typescript
|
|
206
|
-
import { BasicDialog } from '@dsf/dialog/basic-dialog';
|
|
207
|
-
import { Observable } from '@dsf/lib/observable';
|
|
208
279
|
import { AppSettings } from '@dsf/lib/settings';
|
|
280
|
+
import { Observable } from '@dsf/lib/observable';
|
|
209
281
|
|
|
210
|
-
//
|
|
211
|
-
|
|
282
|
+
// AppSettings adds automatic persistence under the given namespace
|
|
283
|
+
class MyModel extends AppSettings {
|
|
212
284
|
constructor() {
|
|
213
|
-
super('
|
|
285
|
+
super('YourName/MyDialog');
|
|
214
286
|
}
|
|
215
287
|
|
|
216
|
-
|
|
217
|
-
|
|
288
|
+
name$ = new Observable<string>();
|
|
289
|
+
enabled$ = new Observable<boolean>(false);
|
|
218
290
|
}
|
|
219
291
|
```
|
|
220
292
|
|
|
221
|
-
#### 2. Build
|
|
293
|
+
#### 2. Build the dialog
|
|
222
294
|
|
|
223
295
|
```typescript
|
|
224
296
|
import { BasicDialog } from '@dsf/dialog/basic-dialog';
|
|
225
|
-
import { MyDialogModel } from './my-dialog-model';
|
|
226
297
|
|
|
227
|
-
|
|
228
|
-
constructor(private readonly model:
|
|
298
|
+
class MyDialog extends BasicDialog {
|
|
299
|
+
constructor(private readonly model: MyModel) {
|
|
229
300
|
super('My Dialog');
|
|
230
301
|
}
|
|
231
302
|
|
|
232
303
|
protected build(): void {
|
|
233
|
-
const add = this
|
|
234
|
-
|
|
304
|
+
const { add, model } = this;
|
|
305
|
+
|
|
306
|
+
add.group('Settings').build(() => {
|
|
307
|
+
add.label('Name:');
|
|
308
|
+
add.edit().value(model.name$);
|
|
235
309
|
|
|
236
|
-
|
|
237
|
-
add.label('Label:');
|
|
238
|
-
add.edit().value(model.nodeLabel$); // Two-way binding
|
|
310
|
+
add.checkbox('Enabled').value(model.enabled$);
|
|
239
311
|
});
|
|
240
312
|
}
|
|
241
313
|
}
|
|
242
314
|
```
|
|
243
315
|
|
|
244
|
-
#### 3.
|
|
316
|
+
#### 3. Show it from a script
|
|
245
317
|
|
|
246
318
|
```typescript
|
|
247
|
-
import { action } from '@dsf/core/action';
|
|
248
|
-
import { getSelectedNode } from '@dsf/helpers/scene-helper';
|
|
249
|
-
import { MyDialog, MyDialogModel } from './my-dialog';
|
|
250
|
-
|
|
251
319
|
action({ text: 'My Dialog Script' }, () => {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if (!selectedNode) {
|
|
256
|
-
console.error('Please select a node');
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
320
|
+
const model = new MyModel();
|
|
321
|
+
const dialog = new MyDialog(model);
|
|
259
322
|
|
|
260
|
-
|
|
261
|
-
model.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
// React to model changes (two-way binding)
|
|
265
|
-
model.nodeLabel$.connect((label) => {
|
|
266
|
-
selectedNode.setLabel(label);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
// Build and show dialog
|
|
270
|
-
const dialog = new MyDialog(model);
|
|
271
|
-
if (dialog.run()) {
|
|
272
|
-
console.log('Dialog accepted');
|
|
273
|
-
} else {
|
|
274
|
-
console.log('Dialog cancelled');
|
|
275
|
-
}
|
|
323
|
+
if (dialog.ok()) {
|
|
324
|
+
// model.name$.value holds whatever the user typed
|
|
325
|
+
}
|
|
276
326
|
});
|
|
277
327
|
```
|
|
278
328
|
|
|
279
|
-
|
|
329
|
+
---
|
|
280
330
|
|
|
281
|
-
|
|
331
|
+
### Observables
|
|
282
332
|
|
|
283
|
-
|
|
333
|
+
`Observable<T>` is lightweight reactive state. Controls bound with `.value(observable)` stay in sync automatically.
|
|
284
334
|
|
|
285
335
|
```typescript
|
|
286
|
-
const name = new Observable<string>('
|
|
336
|
+
const name$ = new Observable<string>('initial');
|
|
287
337
|
|
|
288
338
|
// Subscribe to changes
|
|
289
|
-
name
|
|
339
|
+
name$.connect((value) => console.log(value));
|
|
290
340
|
|
|
291
|
-
// Set value
|
|
292
|
-
name
|
|
341
|
+
// Set value — fires all subscribers
|
|
342
|
+
name$.value = 'updated';
|
|
293
343
|
|
|
294
|
-
//
|
|
295
|
-
name
|
|
296
|
-
(prev, current) => current.toUpperCase() // Transform
|
|
297
|
-
);
|
|
344
|
+
// Transform values before they are applied
|
|
345
|
+
name$.intercept((prev, next) => next.trim());
|
|
298
346
|
|
|
299
|
-
//
|
|
300
|
-
name
|
|
301
|
-
name
|
|
302
|
-
name
|
|
347
|
+
// Batch updates without firing subscribers mid-batch
|
|
348
|
+
name$.pause(() => {
|
|
349
|
+
name$.value = 'a';
|
|
350
|
+
name$.value = 'b'; // only 'b' fires after the pause block
|
|
303
351
|
});
|
|
304
352
|
```
|
|
305
353
|
|
|
306
|
-
|
|
354
|
+
---
|
|
307
355
|
|
|
308
|
-
|
|
356
|
+
### Dialog Builder Reference
|
|
309
357
|
|
|
310
|
-
|
|
311
|
-
- **Nodes**: Type checking (figure, bone, etc.), transforms, visibility, selection
|
|
312
|
-
- **Properties**: Finding, adjusting, interpolating property values
|
|
313
|
-
- **Dialogs**: `BasicDialog`, `InputDialog`, `SelectionDialog`
|
|
314
|
-
- **Arrays**: `distinct()`, `flatten()`, `groupBy()`, unique operations
|
|
315
|
-
- **Strings**: Upper/lowercase, trimming, splitting
|
|
316
|
-
- **Files & Paths**: Reading, writing, directory operations
|
|
317
|
-
- **UI Helpers**: Message boxes, progress dialogs, menus, keyboard shortcuts
|
|
358
|
+
Use `this.add` inside `build()` to construct the UI declaratively.
|
|
318
359
|
|
|
319
|
-
|
|
360
|
+
**Widgets**
|
|
320
361
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
362
|
+
| Builder | Description |
|
|
363
|
+
|---|---|
|
|
364
|
+
| `add.label(text)` | Static text label |
|
|
365
|
+
| `add.edit()` | Single-line text input |
|
|
366
|
+
| `add.button(text)` | Push button |
|
|
367
|
+
| `add.checkbox(text)` | Checkbox |
|
|
368
|
+
| `add.radio(text)` | Radio button |
|
|
369
|
+
| `add.comboBox()` | Drop-down list |
|
|
370
|
+
| `add.listBox()` | Scrollable list |
|
|
371
|
+
| `add.slider(min, max)` | Numeric slider |
|
|
372
|
+
| `add.colorPicker()` | Color picker |
|
|
373
|
+
| `add.nodeSelection()` | Daz Studio node selector |
|
|
325
374
|
|
|
326
|
-
|
|
327
|
-
const figures = allNodes.filter(n => NodeHelper.isFigure(n));
|
|
328
|
-
const unique = ArrayHelper.distinct(figures);
|
|
329
|
-
```
|
|
375
|
+
**Layout**
|
|
330
376
|
|
|
331
|
-
|
|
377
|
+
| Builder | Description |
|
|
378
|
+
|---|---|
|
|
379
|
+
| `add.group(text)` | Group box |
|
|
380
|
+
| `add.tab(text)` | Tab page |
|
|
381
|
+
| `add.horizontal(fn)` | Horizontal layout row |
|
|
382
|
+
| `add.splitter()` | Resizable splitter |
|
|
332
383
|
|
|
333
|
-
|
|
384
|
+
Most widget builders expose a fluent chain:
|
|
334
385
|
|
|
335
386
|
```typescript
|
|
336
|
-
add.
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
add.slider(0, 100).value(opacityObservable);
|
|
341
|
-
});
|
|
387
|
+
add.edit()
|
|
388
|
+
.value(model.name$) // two-way binding
|
|
389
|
+
.toolTip('Enter your name')
|
|
390
|
+
.readOnly(false);
|
|
342
391
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
add.edit().value(nameObservable);
|
|
346
|
-
});
|
|
392
|
+
add.button('Apply')
|
|
393
|
+
.clicked(() => applyChanges());
|
|
347
394
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
.value(
|
|
351
|
-
|
|
395
|
+
add.tab('Options').build(() => {
|
|
396
|
+
add.group('Colors').build(() => {
|
|
397
|
+
add.colorPicker().value(model.color$);
|
|
398
|
+
});
|
|
352
399
|
});
|
|
353
400
|
```
|
|
354
401
|
|
|
355
|
-
|
|
356
|
-
- Basic: Label, Edit (text input), Button, Checkbox, Radio
|
|
357
|
-
- Selection: ComboBox, ListBox, Slider, ColorPicker
|
|
358
|
-
- Layout: Tab, Group, Horizontal, Vertical, Splitter
|
|
359
|
-
- Advanced: ListView, TreeView, Popup Menu
|
|
402
|
+
---
|
|
360
403
|
|
|
361
|
-
###
|
|
404
|
+
### Available Helpers
|
|
362
405
|
|
|
363
|
-
The
|
|
406
|
+
The framework ships helpers for common Daz Studio tasks, all importable from `@dsf/helpers/*`.
|
|
364
407
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
408
|
+
| Module | Key functions |
|
|
409
|
+
|---|---|
|
|
410
|
+
| `scene-helper` | `getRoot()`, `getSelectedNode()`, `getNodes()` |
|
|
411
|
+
| `node-helper` | Type checks (`isFigure`, `isBone`), transforms, visibility |
|
|
412
|
+
| `property-helper` | Find, read, and adjust node properties |
|
|
413
|
+
| `array-helper` | `distinct()`, `flatten()`, `groupBy()` |
|
|
414
|
+
| `string-helper` | Case, trimming, splitting |
|
|
415
|
+
| `directory-helper` | File and path operations |
|
|
416
|
+
| `message-box-helper` | `info()`, `warn()`, `error()` message boxes |
|
|
417
|
+
| `progress-helper` | Progress dialogs |
|
|
418
|
+
| `menu-helper` | Custom menus |
|
|
419
|
+
| `undo-helper` | Undo stack integration |
|
|
368
420
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
});
|
|
421
|
+
```typescript
|
|
422
|
+
import * as SceneHelper from '@dsf/helpers/scene-helper';
|
|
423
|
+
import * as NodeHelper from '@dsf/helpers/node-helper';
|
|
373
424
|
|
|
374
|
-
|
|
375
|
-
model.nodeLabel$.value = 'New Label'; // UI edit box updates
|
|
425
|
+
const figures = SceneHelper.getNodes().filter(n => NodeHelper.isFigure(n));
|
|
376
426
|
```
|
|
377
427
|
|
|
378
|
-
|
|
428
|
+
---
|
|
379
429
|
|
|
380
430
|
### Directory Structure
|
|
381
431
|
|
|
382
|
-
For a typical project using this framework:
|
|
383
|
-
|
|
384
432
|
```
|
|
385
433
|
my-daz-scripts/
|
|
386
434
|
├── src/
|
|
387
|
-
│ ├──
|
|
388
|
-
│
|
|
389
|
-
│
|
|
390
|
-
│
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
├── out/ # Generated launchers, implementations, and copied icons
|
|
394
|
-
├── package.json
|
|
435
|
+
│ ├── hello-world.dsa.ts # runnable entry point → compiles to .dsa
|
|
436
|
+
│ ├── my-dialog-model.ts # plain TypeScript — model or helper class
|
|
437
|
+
│ ├── my-dialog.ts
|
|
438
|
+
│ └── my-dialog-script.dsa.ts # runnable entry point
|
|
439
|
+
├── out/ # build output — launchers, bundles, icons
|
|
440
|
+
├── dazscript.config.ts
|
|
395
441
|
├── tsconfig.json
|
|
396
|
-
└──
|
|
442
|
+
└── package.json
|
|
397
443
|
```
|
|
398
444
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
445
|
+
Files ending in `.dsa.ts` are treated as runnable entry points and compiled to `.dsa`. Plain `.ts` files are modules — imported by entry points but not compiled independently.
|
|
446
|
+
|
|
447
|
+
**Common commands**
|
|
448
|
+
|
|
449
|
+
| Command | What it does |
|
|
450
|
+
|---|---|
|
|
451
|
+
| `npm run build` | Compile TypeScript → Daz Script |
|
|
452
|
+
| `npm run watch` | Recompile on every save |
|
|
453
|
+
| `npm run installer` | Generate the setup dialog |
|
|
454
|
+
| `npm run icons` | Copy icon assets to the output folder |
|
|
406
455
|
|
|
407
|
-
|
|
456
|
+
---
|
|
408
457
|
|
|
409
|
-
|
|
458
|
+
### Development & Publishing
|
|
410
459
|
|
|
411
|
-
|
|
460
|
+
This package uses **semantic-release** for automatic versioning and npm publishing.
|
|
461
|
+
|
|
462
|
+
#### Commit message conventions
|
|
463
|
+
|
|
464
|
+
| Prefix | Effect |
|
|
465
|
+
|---|---|
|
|
466
|
+
| `fix: ...` | Patch bump (`1.0.0` → `1.0.1`) |
|
|
467
|
+
| `feat: ...` | Minor bump (`1.0.0` → `1.1.0`) |
|
|
468
|
+
| `BREAKING CHANGE: ...` in commit body | Major bump (`1.0.0` → `2.0.0`) |
|
|
469
|
+
| No prefix | No version bump |
|
|
470
|
+
|
|
471
|
+
Examples:
|
|
472
|
+
```
|
|
473
|
+
fix: resolve layout overflow in group builder
|
|
474
|
+
feat: add tree view builder
|
|
475
|
+
feat: refactor action entrypoint
|
|
476
|
+
|
|
477
|
+
BREAKING CHANGE: action() now requires an explicit menuPath
|
|
478
|
+
```
|
|
412
479
|
|
|
413
|
-
|
|
480
|
+
#### Publishing
|
|
414
481
|
|
|
415
|
-
|
|
416
|
-
- Bug fixes, patches, or minor improvements
|
|
417
|
-
- Example: `fix: resolve dialog builder layout issue`
|
|
482
|
+
Every push to `master` automatically:
|
|
418
483
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
484
|
+
1. Analyzes commit messages since the last release
|
|
485
|
+
2. Updates the version in `package.json`
|
|
486
|
+
3. Builds the project
|
|
487
|
+
4. Creates a GitHub release with changelog
|
|
488
|
+
5. Publishes to npm
|
|
422
489
|
|
|
423
|
-
|
|
424
|
-
- Add to commit body for breaking changes
|
|
425
|
-
- Example: `feat: refactor action decorator API\n\nBREAKING CHANGE: action() now requires explicit menu path`
|
|
490
|
+
No manual steps required.
|
|
426
491
|
|
|
427
|
-
|
|
428
|
-
- Documentation, style, or non-publishing changes
|
|
429
|
-
- Example: `update README examples`
|
|
492
|
+
---
|
|
430
493
|
|
|
431
|
-
|
|
494
|
+
## Resources
|
|
432
495
|
|
|
433
|
-
|
|
496
|
+
- [DAZ Script Reference](https://docs.daz3d.com/public/software/dazstudio/4/referenceguide/scripting/start) — official Daz Studio scripting documentation
|
|
497
|
+
- [dazscript-types](https://www.npmjs.com/package/dazscript-types) — TypeScript type definitions for the Daz Studio API
|
|
434
498
|
|
|
435
|
-
|
|
436
|
-
2. **Update** version in `package.json`
|
|
437
|
-
3. **Build** the project (`npm run build`)
|
|
438
|
-
4. **Create** a GitHub release with changelog
|
|
439
|
-
5. **Publish** to npm
|
|
499
|
+
## Examples
|
|
440
500
|
|
|
441
|
-
|
|
501
|
+
The `src/examples/` folder contains ready-to-run scripts demonstrating common patterns and fuller reference implementations for common Daz Studio workflows.
|