dazscript-framework 0.2.5 → 0.3.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 +57 -1
- package/dist/scripts/app-data-path.js +50 -0
- package/dist/scripts/init.js +10 -0
- package/dist/scripts/install-generator.js +39 -39
- package/dist/scripts/launchers.js +1 -42
- package/package.json +2 -1
- package/src/{Install.dsa.ts → Setup.dsa.ts} +3 -3
- package/src/helpers/action-helper.ts +14 -1
- package/src/helpers/custom-action-helper.ts +375 -84
- package/src/helpers/custom-action-installer-helper.ts +332 -0
- package/src/shared/set-keyboard-shortcut.ts +29 -5
- package/webpack.config.js +2 -1
- package/src/Uninstall.dsa.ts +0 -19
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ The **DazScript Framework** is a TypeScript-based framework for writing Daz Stud
|
|
|
14
14
|
|
|
15
15
|
- TypeScript support with full IntelliSense.
|
|
16
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.
|
|
17
19
|
- Easy integration with Daz Studio for quick script deployment.
|
|
18
20
|
|
|
19
21
|
## Installation
|
|
@@ -65,9 +67,12 @@ export default defineConfig({
|
|
|
65
67
|
outDir: './out',
|
|
66
68
|
defaultMenuPath: '/MyScripts',
|
|
67
69
|
appDataPath: 'YourName/my-project',
|
|
70
|
+
bundleName: 'My Project',
|
|
68
71
|
});
|
|
69
72
|
```
|
|
70
73
|
|
|
74
|
+
`bundleName` is optional display metadata for generated setup dialogs. If omitted, the dialog falls back to `Setup Scripts`. `dazscript init` now scaffolds it automatically from the project folder name.
|
|
75
|
+
|
|
71
76
|
Built action outputs now use stable launcher shims by default:
|
|
72
77
|
|
|
73
78
|
- `out/<script>.dsa` is the stable launcher registered with Daz Studio menus, toolbars, and shortcuts
|
|
@@ -129,7 +134,7 @@ Common `action(...)` parameters:
|
|
|
129
134
|
- `toolbar`: the toolbar name used when the action should appear on a toolbar.
|
|
130
135
|
- `group`: an optional grouping label used by Daz Studio for related actions.
|
|
131
136
|
- `description`: a longer description for the action.
|
|
132
|
-
- `bundle`: generates
|
|
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`.
|
|
133
138
|
|
|
134
139
|
When an action is built, the framework emits two files for it:
|
|
135
140
|
|
|
@@ -140,6 +145,57 @@ Generated installers register the launcher path, so menu placement, toolbars, sh
|
|
|
140
145
|
|
|
141
146
|
If the local `lib/` implementation is missing, the launcher falls back to the configured `appDataPath`. Builds now require this value and validate it as a unique `Author/Product` style path.
|
|
142
147
|
|
|
148
|
+
### Generated Setup Script
|
|
149
|
+
|
|
150
|
+
Running `npm run installer` generates `src/Setup.dsa.ts` for the project.
|
|
151
|
+
|
|
152
|
+
This flow is completely automated. The installer generator scans runnable `.dsa.ts` entry files, reads the top-level `action(...)` call, and derives the setup dialog rows and registration behavior directly from that metadata. In practice, the menu path, toolbar target, shortcut, description, grouping, icon usage, and bundle-specific setup outputs all come from the action definition rather than from separate installer code you have to maintain by hand.
|
|
153
|
+
|
|
154
|
+
The generated setup script:
|
|
155
|
+
|
|
156
|
+
- Scans all runnable top-level `.dsa.ts` files under `scriptsPath`
|
|
157
|
+
- Reads `action(...)` metadata directly from the source
|
|
158
|
+
- Normalizes default menu paths relative to `defaultMenuPath`
|
|
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
|
|
163
|
+
|
|
164
|
+
The setup dialog initializes from the current Daz Studio install state rather than assuming a clean install. It checks which actions are already installed, which ones are present in menus or toolbars, and what shortcut is currently assigned.
|
|
165
|
+
|
|
166
|
+
Current setup dialog behavior:
|
|
167
|
+
|
|
168
|
+
- Shows an install checkbox plus the columns `Action`, `Shortcut`, `Description`, `Menu`, and `Toolbar`
|
|
169
|
+
- Includes a search box that filters by action name, shortcut, description, menu path, and toolbar
|
|
170
|
+
- Supports `Select All` and `Deselect All` for the currently visible rows
|
|
171
|
+
- Lets the user right-click an action to set a shortcut or reset it to the default shortcut
|
|
172
|
+
- Shows shortcut overrides with an `[ovr]` marker
|
|
173
|
+
- Displays the configured toolbar name directly instead of a generic yes/no flag
|
|
174
|
+
- Uses the configured `bundleName` in the window title when available
|
|
175
|
+
|
|
176
|
+
Applying the setup dialog does both install and cleanup work:
|
|
177
|
+
|
|
178
|
+
- Selected rows are installed or updated through the framework custom action helpers
|
|
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
|
|
182
|
+
|
|
183
|
+
The generated project-level setup file replaces the older generated `Install.dsa.ts` and `Uninstall.dsa.ts` flow. The installer generator now removes those legacy files if they still exist.
|
|
184
|
+
|
|
185
|
+
### Action-Level Bundles
|
|
186
|
+
|
|
187
|
+
The `bundle` property on `action(...)` is separate from project `bundleName`.
|
|
188
|
+
|
|
189
|
+
- `bundleName` in `dazscript.config.ts` is project metadata used for the setup dialog title
|
|
190
|
+
- `bundle` in an action definition changes installer generation behavior for that action
|
|
191
|
+
|
|
192
|
+
When `bundle` is set on an action, the installer generator also writes a setup script beside that action:
|
|
193
|
+
|
|
194
|
+
- `bundle: true` writes `Setup.dsa.ts`
|
|
195
|
+
- `bundle: 'Utilities'` writes `Setup Utilities.dsa.ts`
|
|
196
|
+
|
|
197
|
+
Those bundle-generated setup files use the same setup dialog helper and now also receive the project `bundleName`.
|
|
198
|
+
|
|
143
199
|
### Building UIs with Observables & Dialogs
|
|
144
200
|
|
|
145
201
|
The framework uses a **Model-View pattern** with reactive data bindings:
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function toPosix(filePath) {
|
|
4
|
+
return filePath.replace(/\\/g, '/');
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function validateAppDataPath(appDataPath, workdir) {
|
|
8
|
+
if (!appDataPath || typeof appDataPath !== 'string') {
|
|
9
|
+
throw new Error(
|
|
10
|
+
`[dazscript] Missing required appDataPath in ${workdir}. ` +
|
|
11
|
+
`Set appDataPath: 'Author/Product' in dazscript.config.ts.`
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const normalized = toPosix(appDataPath).trim().replace(/^\/+|\/+$/g, '');
|
|
16
|
+
const segments = normalized.split('/').filter(Boolean);
|
|
17
|
+
|
|
18
|
+
if (segments.length < 2) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`[dazscript] Invalid appDataPath "${appDataPath}" in ${workdir}. ` +
|
|
21
|
+
`Use at least two segments, for example 'Author/Product'.`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const blockedSegments = new Set([
|
|
26
|
+
'appdata',
|
|
27
|
+
'cache',
|
|
28
|
+
'data',
|
|
29
|
+
'lib',
|
|
30
|
+
'libs',
|
|
31
|
+
'script',
|
|
32
|
+
'scripts',
|
|
33
|
+
'temp',
|
|
34
|
+
'tmp',
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const invalidSegment = segments.find((segment) => blockedSegments.has(segment.toLowerCase()));
|
|
38
|
+
if (invalidSegment) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`[dazscript] Invalid appDataPath "${appDataPath}" in ${workdir}. ` +
|
|
41
|
+
`Path segments like "${invalidSegment}" are too generic. Use a unique Author/Product path.`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return normalized;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
validateAppDataPath,
|
|
50
|
+
};
|
package/dist/scripts/init.js
CHANGED
|
@@ -50,10 +50,19 @@ export default defineConfig({
|
|
|
50
50
|
outDir: '${options.outDir}',
|
|
51
51
|
defaultMenuPath: '${options.menuPath}',
|
|
52
52
|
appDataPath: '${options.appDataPath}',
|
|
53
|
+
bundleName: '${options.bundleName}',
|
|
53
54
|
});
|
|
54
55
|
`;
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
function toBundleName(projectName) {
|
|
59
|
+
return projectName
|
|
60
|
+
.split(/[-_\s]+/)
|
|
61
|
+
.filter(Boolean)
|
|
62
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
63
|
+
.join(' ');
|
|
64
|
+
}
|
|
65
|
+
|
|
57
66
|
function buildTsconfigContent() {
|
|
58
67
|
return `{
|
|
59
68
|
"extends": "./node_modules/dazscript-framework/tsconfig.json",
|
|
@@ -102,6 +111,7 @@ function initProject(workdir, rawOptions) {
|
|
|
102
111
|
scriptsPath: normalizePath(rawOptions.scriptsPath, './src'),
|
|
103
112
|
outDir: normalizePath(rawOptions.outDir, './out'),
|
|
104
113
|
appDataPath: normalizePath(rawOptions.appDataPath, `YourName/${projectName}`),
|
|
114
|
+
bundleName: toBundleName(projectName),
|
|
105
115
|
};
|
|
106
116
|
|
|
107
117
|
writeFileIfNeeded(
|
|
@@ -2,6 +2,8 @@ const ts = require('typescript');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const glob = require('glob');
|
|
5
|
+
const { loadConfig } = require('./config-loader');
|
|
6
|
+
const { validateAppDataPath } = require('./app-data-path');
|
|
5
7
|
|
|
6
8
|
const nameofActionFunction = 'action';
|
|
7
9
|
|
|
@@ -19,19 +21,14 @@ function stringOrDefault(str, defaultValue) {
|
|
|
19
21
|
return str !== undefined && str !== null && str !== '' ? str : defaultValue;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
function generateInstallerTemplate(data) {
|
|
24
|
+
function generateInstallerTemplate(data, settingsPath, bundleName) {
|
|
23
25
|
return `
|
|
24
|
-
import {
|
|
26
|
+
import { showSetupCustomActionsDialog as setup } from '@dsf/helpers/custom-action-installer-helper';
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
function generateUninstallerTemplate(data) {
|
|
31
|
-
return `
|
|
32
|
-
import { uninstallCustomActions as uninstall } from '@dsf/helpers/custom-action-helper';
|
|
33
|
-
|
|
34
|
-
uninstall(${data});
|
|
28
|
+
setup(${data}, ${JSON.stringify({
|
|
29
|
+
settingsPath,
|
|
30
|
+
bundleName,
|
|
31
|
+
})});
|
|
35
32
|
`;
|
|
36
33
|
}
|
|
37
34
|
|
|
@@ -154,7 +151,7 @@ function findActionEntryFiles(workdir, options) {
|
|
|
154
151
|
});
|
|
155
152
|
}
|
|
156
153
|
|
|
157
|
-
function processScript(filePath, container, defaultMenuPath) {
|
|
154
|
+
function processScript(filePath, container, defaultMenuPath, settingsPath, bundleName) {
|
|
158
155
|
const fileInfo = path.parse(filePath);
|
|
159
156
|
const content = fs.readFileSync(filePath, 'utf-8').toString();
|
|
160
157
|
const actionCall = findTopLevelActionCall(content, filePath);
|
|
@@ -208,38 +205,29 @@ function processScript(filePath, container, defaultMenuPath) {
|
|
|
208
205
|
container.scripts.push(script);
|
|
209
206
|
|
|
210
207
|
if (decorator.bundle !== undefined) {
|
|
211
|
-
let
|
|
212
|
-
let packageUninstallerFilePath = `Uninstall.dsa.ts`;
|
|
208
|
+
let packageSetupFilePath = `Setup.dsa.ts`;
|
|
213
209
|
|
|
214
210
|
if (decorator.bundle !== true) {
|
|
215
|
-
|
|
216
|
-
packageUninstallerFilePath = `Uninstall ${decorator.bundle}.dsa.ts`;
|
|
211
|
+
packageSetupFilePath = `Setup ${decorator.bundle}.dsa.ts`;
|
|
217
212
|
}
|
|
218
213
|
|
|
219
214
|
let bundleScriptContent = generateInstallerTemplate(
|
|
220
|
-
JSON.stringify(container.scripts, null, 4)
|
|
215
|
+
JSON.stringify(container.scripts, null, 4),
|
|
216
|
+
settingsPath,
|
|
217
|
+
bundleName
|
|
221
218
|
);
|
|
222
219
|
let bundleScriptFilePath = path.join(
|
|
223
220
|
path.parse(filePath).dir,
|
|
224
|
-
|
|
225
|
-
);
|
|
226
|
-
fs.writeFileSync(bundleScriptFilePath, bundleScriptContent);
|
|
227
|
-
|
|
228
|
-
bundleScriptContent = generateUninstallerTemplate(
|
|
229
|
-
JSON.stringify(container.scripts, null, 4)
|
|
230
|
-
);
|
|
231
|
-
bundleScriptFilePath = path.join(
|
|
232
|
-
path.parse(filePath).dir,
|
|
233
|
-
packageUninstallerFilePath
|
|
221
|
+
packageSetupFilePath
|
|
234
222
|
);
|
|
235
223
|
fs.writeFileSync(bundleScriptFilePath, bundleScriptContent);
|
|
236
224
|
}
|
|
237
225
|
}
|
|
238
226
|
|
|
239
|
-
function processScripts(paths, container, defaultMenuPath) {
|
|
227
|
+
function processScripts(paths, container, defaultMenuPath, settingsPath, bundleName) {
|
|
240
228
|
paths.forEach((filePath) => {
|
|
241
229
|
console.log(`Processing ${filePath}`);
|
|
242
|
-
processScript(filePath, container, defaultMenuPath);
|
|
230
|
+
processScript(filePath, container, defaultMenuPath, settingsPath, bundleName);
|
|
243
231
|
});
|
|
244
232
|
}
|
|
245
233
|
|
|
@@ -247,10 +235,16 @@ function generateInstallerFiles(workdir, options) {
|
|
|
247
235
|
const defaultMenuPath = options.defaultMenuPath.endsWith('/')
|
|
248
236
|
? options.defaultMenuPath
|
|
249
237
|
: `${options.defaultMenuPath}/`;
|
|
238
|
+
const { config } = loadConfig(workdir);
|
|
239
|
+
const appDataPath = validateAppDataPath(options.appDataPath || config.appDataPath, workdir);
|
|
240
|
+
const settingsPath = `${appDataPath}/Installer`;
|
|
241
|
+
const bundleName = typeof config.bundleName === 'string' && config.bundleName.trim()
|
|
242
|
+
? config.bundleName.trim()
|
|
243
|
+
: undefined;
|
|
250
244
|
const container = { scripts: [] };
|
|
251
245
|
const matches = findActionEntryFiles(workdir, options);
|
|
252
246
|
|
|
253
|
-
processScripts(matches, container, defaultMenuPath);
|
|
247
|
+
processScripts(matches, container, defaultMenuPath, settingsPath, bundleName);
|
|
254
248
|
|
|
255
249
|
container.scripts = container.scripts.sort((a, b) => {
|
|
256
250
|
const aKey = a.menuPath + a.filePath;
|
|
@@ -259,17 +253,16 @@ function generateInstallerFiles(workdir, options) {
|
|
|
259
253
|
});
|
|
260
254
|
|
|
261
255
|
const installerScriptContent = generateInstallerTemplate(
|
|
262
|
-
JSON.stringify(container.scripts, null, 4)
|
|
256
|
+
JSON.stringify(container.scripts, null, 4),
|
|
257
|
+
settingsPath,
|
|
258
|
+
bundleName
|
|
263
259
|
);
|
|
264
|
-
fs.writeFileSync(path.join(workdir, 'src', '
|
|
260
|
+
fs.writeFileSync(path.join(workdir, 'src', 'Setup.dsa.ts'), installerScriptContent);
|
|
265
261
|
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
);
|
|
269
|
-
fs.
|
|
270
|
-
path.join(workdir, 'src', 'Uninstall.dsa.ts'),
|
|
271
|
-
uninstallerScriptContent
|
|
272
|
-
);
|
|
262
|
+
const installPath = path.join(workdir, 'src', 'Install.dsa.ts');
|
|
263
|
+
const uninstallPath = path.join(workdir, 'src', 'Uninstall.dsa.ts');
|
|
264
|
+
if (fs.existsSync(installPath)) fs.unlinkSync(installPath);
|
|
265
|
+
if (fs.existsSync(uninstallPath)) fs.unlinkSync(uninstallPath);
|
|
273
266
|
}
|
|
274
267
|
|
|
275
268
|
if (require.main === module) {
|
|
@@ -277,6 +270,7 @@ if (require.main === module) {
|
|
|
277
270
|
const options = {
|
|
278
271
|
scriptsPath: './src',
|
|
279
272
|
defaultMenuPath: 'My Scripts',
|
|
273
|
+
appDataPath: undefined,
|
|
280
274
|
};
|
|
281
275
|
|
|
282
276
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -294,6 +288,12 @@ if (require.main === module) {
|
|
|
294
288
|
continue;
|
|
295
289
|
}
|
|
296
290
|
|
|
291
|
+
if (arg === '--app-data-path') {
|
|
292
|
+
options.appDataPath = args[index + 1];
|
|
293
|
+
index += 1;
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
297
|
throw new Error(`Unknown option: ${arg}`);
|
|
298
298
|
}
|
|
299
299
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { findActionEntryFiles } = require('./install-generator');
|
|
6
|
+
const { validateAppDataPath } = require('./app-data-path');
|
|
6
7
|
|
|
7
8
|
function toPosix(filePath) {
|
|
8
9
|
return filePath.replace(/\\/g, '/');
|
|
@@ -25,47 +26,6 @@ function getImplementationRelativePath(outputRelativePath) {
|
|
|
25
26
|
return path.posix.join(implementationDirectory, 'script.dsa');
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
function validateAppDataPath(appDataPath, workdir) {
|
|
29
|
-
if (!appDataPath || typeof appDataPath !== 'string') {
|
|
30
|
-
throw new Error(
|
|
31
|
-
`[dazscript] Missing required appDataPath in ${workdir}. ` +
|
|
32
|
-
`Set appDataPath: 'Author/Product' in dazscript.config.ts.`
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const normalized = toPosix(appDataPath).trim().replace(/^\/+|\/+$/g, '');
|
|
37
|
-
const segments = normalized.split('/').filter(Boolean);
|
|
38
|
-
|
|
39
|
-
if (segments.length < 2) {
|
|
40
|
-
throw new Error(
|
|
41
|
-
`[dazscript] Invalid appDataPath "${appDataPath}" in ${workdir}. ` +
|
|
42
|
-
`Use at least two segments, for example 'Author/Product'.`
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const blockedSegments = new Set([
|
|
47
|
-
'appdata',
|
|
48
|
-
'cache',
|
|
49
|
-
'data',
|
|
50
|
-
'lib',
|
|
51
|
-
'libs',
|
|
52
|
-
'script',
|
|
53
|
-
'scripts',
|
|
54
|
-
'temp',
|
|
55
|
-
'tmp',
|
|
56
|
-
]);
|
|
57
|
-
|
|
58
|
-
const invalidSegment = segments.find((segment) => blockedSegments.has(segment.toLowerCase()));
|
|
59
|
-
if (invalidSegment) {
|
|
60
|
-
throw new Error(
|
|
61
|
-
`[dazscript] Invalid appDataPath "${appDataPath}" in ${workdir}. ` +
|
|
62
|
-
`Path segments like "${invalidSegment}" are too generic. Use a unique Author/Product path.`
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return normalized;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
29
|
function makeLauncherSource(implementationRelativePath, appDataImplementationRelativePath) {
|
|
70
30
|
return [
|
|
71
31
|
'// Auto-generated launcher shim.',
|
|
@@ -158,5 +118,4 @@ function createActionLaunchers(workdir, options) {
|
|
|
158
118
|
|
|
159
119
|
module.exports = {
|
|
160
120
|
createActionLaunchers,
|
|
161
|
-
validateAppDataPath,
|
|
162
121
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dazscript-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"author": "Freddy Diaz",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"description": "",
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@types/node": "^22.10.5",
|
|
70
|
+
"dazscript-types": "^0.2.4",
|
|
70
71
|
"eslint": "^9.17.0",
|
|
71
72
|
"typescript": "^5.7.3"
|
|
72
73
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
2
|
+
import { showSetupCustomActionsDialog as setup } from '@dsf/helpers/custom-action-installer-helper';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
setup([
|
|
5
5
|
{
|
|
6
6
|
"name": null,
|
|
7
7
|
"text": "Hello World",
|
|
@@ -16,4 +16,4 @@ install([
|
|
|
16
16
|
"menuPath": "/DazScriptFramework/samples",
|
|
17
17
|
"description": "sample-dialog"
|
|
18
18
|
}
|
|
19
|
-
]);
|
|
19
|
+
], {"settingsPath":"DazScriptFramework/samples/Installer","bundleName":"Samples"});
|
|
@@ -37,7 +37,7 @@ const shortcutTokenMap: { [key: string]: string } = {
|
|
|
37
37
|
'DOWN': 'Down',
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const normalizeShortcut = (shortcut: string): string => {
|
|
40
|
+
export const normalizeShortcut = (shortcut: string): string => {
|
|
41
41
|
if (!shortcut) return ''
|
|
42
42
|
|
|
43
43
|
return shortcut
|
|
@@ -53,6 +53,19 @@ const normalizeShortcut = (shortcut: string): string => {
|
|
|
53
53
|
.join('+')
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
export const getActionShortcut = (name: string): string => {
|
|
57
|
+
const action = findAction(name)
|
|
58
|
+
if (!action) return ''
|
|
59
|
+
|
|
60
|
+
if (isCustomAction(action)) {
|
|
61
|
+
const index = actionMgr.findCustomAction(name)
|
|
62
|
+
if (index < 0) return ''
|
|
63
|
+
return normalizeShortcut(String(actionMgr.getCustomActionShortcut(index) ?? '').trim())
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return normalizeShortcut(String(action.shortcut?.toString() ?? '').trim())
|
|
67
|
+
}
|
|
68
|
+
|
|
56
69
|
type ActionShortcutEntry = {
|
|
57
70
|
action: DzAction
|
|
58
71
|
shortcut: string
|