@enfocussw/switch-scripting-context 25.11.1-beta.3 → 25.11.1-beta.4
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/CHANGELOG.md +31 -0
- package/README.md +14 -9
- package/dist/init.d.ts +7 -0
- package/dist/init.js +59 -9
- package/docs/switch-api/logging.md +7 -0
- package/docs/switch-appstore/app-guidelines.md +8 -4
- package/docs/switch-project/property-editors.md +46 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,37 @@ All notable changes to this package are documented here. Format follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [25.11.1-beta.4] - 2026-09-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Guidance in `docs/switch-project/property-editors.md` for a property whose shape varies by a
|
|
13
|
+
type selector. Declare an enum master and one dependent per shape instead of encoding JSON in a
|
|
14
|
+
single property, so each shape gets a native editor and most parsing disappears. Includes the
|
|
15
|
+
two constraints: the master must be static, and a hidden dependent throws when read.
|
|
16
|
+
- A rule in `docs/switch-api/logging.md` against adding a script-level verbosity property. Switch
|
|
17
|
+
already gates `Debug` behind a preference, so the property duplicates a control the user has and
|
|
18
|
+
costs a pane row and translated strings.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- `init` and `remove` now write to a docs folder named `docs-for-agents` instead of
|
|
23
|
+
`switch-docs`. Re-running `init` after an upgrade deletes the old folder, drops its gitignore
|
|
24
|
+
line, and repoints the generated AI config files, so no stale copy of the docs is left for an
|
|
25
|
+
agent to load. Pass `--docs-dir switch-docs` to keep the old name.
|
|
26
|
+
- The `DetailedInfo` item in `docs/switch-appstore/app-guidelines.md` no longer reads as requiring
|
|
27
|
+
it on every property and connection. It is optional, matching what the property documentation
|
|
28
|
+
and app manual guidance already said, and points at the app manual as the surface users read.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- The secret property row in `docs/switch-project/property-editors.md` listed `password` as an
|
|
33
|
+
editor chain. It is a `Type`, and there is no such editor token, so following the row produced an
|
|
34
|
+
invalid declaration.
|
|
35
|
+
- The array property row in `docs/switch-project/property-editors.md` claimed the chain always
|
|
36
|
+
returns a `string[]`, which the modal editor table contradicts. Both files now say the behaviour
|
|
37
|
+
is unverified and that code should handle a bare string too.
|
|
38
|
+
|
|
8
39
|
## [25.11.1-beta.3] - 2026-09-17
|
|
9
40
|
|
|
10
41
|
### Added
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ AI coding assistant context for [Enfocus Switch](https://www.enfocus.com/en/swit
|
|
|
4
4
|
|
|
5
5
|
Installs curated API reference docs and generates config files for 8 AI coding agents (Claude Code, GitHub Copilot, Cursor, Codex CLI/OpenCode, Gemini CLI, Windsurf, Zed, and Cline) so AI assistants understand the Switch scripting API out of the box.
|
|
6
6
|
|
|
7
|
-
See [CHANGELOG.md](https://cdn.jsdelivr.net/npm/@enfocussw/switch-scripting-context@25.11.1-beta.
|
|
7
|
+
See [CHANGELOG.md](https://cdn.jsdelivr.net/npm/@enfocussw/switch-scripting-context@25.11.1-beta.4/CHANGELOG.md) (also included in this package) for what's changed between versions.
|
|
8
8
|
|
|
9
9
|
## Usage
|
|
10
10
|
|
|
@@ -45,20 +45,25 @@ so the beta line precedes GA and leaves `25.11.0` free for the first published r
|
|
|
45
45
|
|
|
46
46
|
## What it does
|
|
47
47
|
|
|
48
|
-
1. Copies the Switch API reference docs to `
|
|
48
|
+
1. Copies the Switch API reference docs to `docs-for-agents/` in your project
|
|
49
49
|
2. Generates AI config files for each tool (see below)
|
|
50
|
-
3. Appends `
|
|
50
|
+
3. Appends `docs-for-agents/` to `.gitignore`
|
|
51
|
+
|
|
52
|
+
Versions before this one used `switch-docs/` as the folder name. Re-running `init` deletes that
|
|
53
|
+
folder and its `.gitignore` line, so your agents do not find a second, stale copy of the docs. It
|
|
54
|
+
only deletes a `switch-docs/` folder that `init` itself wrote. Pass `--docs-dir switch-docs` to
|
|
55
|
+
stay on the old name.
|
|
51
56
|
|
|
52
57
|
### Generated files
|
|
53
58
|
|
|
54
59
|
| Tool | File | How context is loaded |
|
|
55
60
|
|---|---|---|
|
|
56
|
-
| Claude Code / ClawCode | `CLAUDE.md` | `@
|
|
61
|
+
| Claude Code / ClawCode | `CLAUDE.md` | `@docs-for-agents/switch-scripting.md` import; hub file routes to specific API docs on demand |
|
|
57
62
|
| GitHub Copilot | `.github/copilot-instructions.md` | `#file:` reference to hub |
|
|
58
63
|
| GitHub Copilot (scoped) | `.github/instructions/switch-scripting.instructions.md` | `applyTo: "**/*.ts"`; auto-attaches to every TypeScript file edit |
|
|
59
64
|
| Cursor | `.cursor/rules/switch-scripting.mdc` | Inline key rules + full API file path list |
|
|
60
65
|
| Codex CLI / OpenCode | `AGENTS.md` | Inline key rules + full API file path list |
|
|
61
|
-
| Gemini CLI | `GEMINI.md` | `@
|
|
66
|
+
| Gemini CLI | `GEMINI.md` | `@docs-for-agents/switch-scripting.md` import |
|
|
62
67
|
| Windsurf | `.windsurfrules` | Inline key rules + full API file path list |
|
|
63
68
|
| Zed | `.rules` | Inline key rules + full API file path list |
|
|
64
69
|
| Cline | `.clinerules` | Inline key rules + full API file path list |
|
|
@@ -84,7 +89,7 @@ A few things this gets you without asking for them by name:
|
|
|
84
89
|
properties (app path/licence) are off-limits and left to SwitchScripter's GUI instead.
|
|
85
90
|
|
|
86
91
|
Re-run `init` after upgrading this package so the copied docs and generated config files catch up.
|
|
87
|
-
See [CHANGELOG.md](https://cdn.jsdelivr.net/npm/@enfocussw/switch-scripting-context@25.11.1-beta.
|
|
92
|
+
See [CHANGELOG.md](https://cdn.jsdelivr.net/npm/@enfocussw/switch-scripting-context@25.11.1-beta.4/CHANGELOG.md) for what changed.
|
|
88
93
|
|
|
89
94
|
## Options
|
|
90
95
|
|
|
@@ -94,7 +99,7 @@ npx switch-scripting-context init [options]
|
|
|
94
99
|
--tools <list> Tools to configure. Default: all
|
|
95
100
|
IDs: claude, copilot, cursor, codex, gemini, windsurf, zed, cline
|
|
96
101
|
Aliases: opencode (→ codex), clawcode (→ claude)
|
|
97
|
-
--docs-dir <dir> Destination folder for docs. Default:
|
|
102
|
+
--docs-dir <dir> Destination folder for docs. Default: docs-for-agents
|
|
98
103
|
--force Overwrite existing AI config files instead of merging
|
|
99
104
|
--dry-run Print what would happen without writing any files
|
|
100
105
|
```
|
|
@@ -124,7 +129,7 @@ To undo `init`, run `remove`. By default it deletes the docs folder, the Switch
|
|
|
124
129
|
npx switch-scripting-context remove [options]
|
|
125
130
|
|
|
126
131
|
--tools <list> Only remove these tools' config. Default: remove everything
|
|
127
|
-
--docs-dir <dir> Docs folder to remove. Default:
|
|
132
|
+
--docs-dir <dir> Docs folder to remove. Default: docs-for-agents
|
|
128
133
|
--dry-run Print what would happen without writing any files
|
|
129
134
|
```
|
|
130
135
|
|
|
@@ -143,7 +148,7 @@ npm install --save-dev "https://github.com/enfocus-switch/types-switch-scripting
|
|
|
143
148
|
|
|
144
149
|
## Included docs
|
|
145
150
|
|
|
146
|
-
The `
|
|
151
|
+
The `docs-for-agents/` folder contains:
|
|
147
152
|
|
|
148
153
|
- `switch-scripting.md`: master index with execution environment rules and "load when" routing table
|
|
149
154
|
<!-- docs-index:readme begin -->
|
package/dist/init.d.ts
CHANGED
|
@@ -35,4 +35,11 @@ export declare function findMarkerRange(content: string): [number, number] | nul
|
|
|
35
35
|
export declare function packageVersion(root?: string): string;
|
|
36
36
|
/** The comment init writes as the first line of the copied hub file. */
|
|
37
37
|
export declare function versionStamp(version: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Drop the `docsDir/` line init added, together with the comment line directly above it.
|
|
40
|
+
* The comment and the path are matched as one unit so that a project carrying entries for
|
|
41
|
+
* two docs folders (one left by an older version, one current) keeps the comment belonging
|
|
42
|
+
* to the entry that stays.
|
|
43
|
+
*/
|
|
44
|
+
export declare function stripGitignoreEntry(content: string, docsDir: string): string;
|
|
38
45
|
export declare function run(argv?: string[]): Promise<void>;
|
package/dist/init.js
CHANGED
|
@@ -44,6 +44,7 @@ exports.buildIdMap = buildIdMap;
|
|
|
44
44
|
exports.findMarkerRange = findMarkerRange;
|
|
45
45
|
exports.packageVersion = packageVersion;
|
|
46
46
|
exports.versionStamp = versionStamp;
|
|
47
|
+
exports.stripGitignoreEntry = stripGitignoreEntry;
|
|
47
48
|
exports.run = run;
|
|
48
49
|
const fs = __importStar(require("fs"));
|
|
49
50
|
const path = __importStar(require("path"));
|
|
@@ -92,6 +93,13 @@ const INLINE_CORE_RULES = `## Core rules
|
|
|
92
93
|
- After editing \`main.ts\`, transpile with \`SwitchScriptTool --transpile <folder>\` — do not use \`tsc\` directly.
|
|
93
94
|
- Never use a script folder in production — pack with \`SwitchScriptTool --pack\` first; folders are excluded from flow exports.`;
|
|
94
95
|
// ─── Content generators ──────────────────────────────────────────────────────
|
|
96
|
+
// Claude Code and Gemini CLI resolve @file imports themselves, so their generated
|
|
97
|
+
// block is just a pointer to switch-scripting.md; the routing table and key rules
|
|
98
|
+
// living there are the actual instructions. Codex, Windsurf, Zed, and Cline don't
|
|
99
|
+
// reliably follow that import syntax, so their generators (generateInlineBlock)
|
|
100
|
+
// paste INLINE_CORE_RULES and the routing table directly into the generated file
|
|
101
|
+
// instead. Don't collapse these into one shared generator; the split is deliberate,
|
|
102
|
+
// not duplication.
|
|
95
103
|
function generateClaudeMd(docsDir) {
|
|
96
104
|
return `${MARKER_BEGIN}
|
|
97
105
|
## Switch Scripting (Enfocus Switch)
|
|
@@ -395,6 +403,33 @@ function stampDocsVersion(packageRoot, destDir, dryRun) {
|
|
|
395
403
|
fs.writeFileSync(hubPath, `${stamp}\n${fs.readFileSync(hubPath, 'utf8')}`, 'utf8');
|
|
396
404
|
console.log(` ${rel} → stamped ${stamp}`);
|
|
397
405
|
}
|
|
406
|
+
// ─── Legacy docs folder ──────────────────────────────────────────────────────
|
|
407
|
+
/** Docs folder name written by versions before the rename to docs-for-agents. */
|
|
408
|
+
const LEGACY_DOCS_DIR = 'switch-docs';
|
|
409
|
+
/**
|
|
410
|
+
* Delete the docs folder an older version wrote under its old name.
|
|
411
|
+
*
|
|
412
|
+
* init regenerates every AI config block in place, so after an upgrade the configs point at
|
|
413
|
+
* the new folder and nothing reads the old one. It is gitignored, so nothing surfaces it
|
|
414
|
+
* either, but an agent grepping the project still finds and loads the stale copy. The folder
|
|
415
|
+
* is only removed when it holds the hub file init writes, so a folder of the same name that
|
|
416
|
+
* this tool did not create is left alone.
|
|
417
|
+
*/
|
|
418
|
+
function removeLegacyDocsDir(targetRoot, docsDir, dryRun) {
|
|
419
|
+
if (docsDir === LEGACY_DOCS_DIR)
|
|
420
|
+
return;
|
|
421
|
+
const legacyDir = path.join(targetRoot, LEGACY_DOCS_DIR);
|
|
422
|
+
if (!fs.existsSync(path.join(legacyDir, HUB_FILE)))
|
|
423
|
+
return;
|
|
424
|
+
if (dryRun) {
|
|
425
|
+
console.log(` [dry-run] ${LEGACY_DOCS_DIR}/ → deleted (docs folder from an earlier version)`);
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
fs.rmSync(legacyDir, { recursive: true, force: true });
|
|
429
|
+
console.log(` ${LEGACY_DOCS_DIR}/ → deleted (docs folder from an earlier version)`);
|
|
430
|
+
}
|
|
431
|
+
removeGitignoreEntry(targetRoot, LEGACY_DOCS_DIR, dryRun);
|
|
432
|
+
}
|
|
398
433
|
// ─── .gitignore update ───────────────────────────────────────────────────────
|
|
399
434
|
function updateGitignore(targetRoot, docsDir, dryRun) {
|
|
400
435
|
const gitignorePath = path.join(targetRoot, '.gitignore');
|
|
@@ -408,7 +443,10 @@ function updateGitignore(targetRoot, docsDir, dryRun) {
|
|
|
408
443
|
console.log(` [dry-run] .gitignore → append ${docsDir}/ entry`);
|
|
409
444
|
return;
|
|
410
445
|
}
|
|
411
|
-
|
|
446
|
+
// entry opens with its own blank-line separator, so normalise what the file already
|
|
447
|
+
// ends with. Without this, appending after an entry that was just removed leaves a
|
|
448
|
+
// widening gap in the file on every upgrade.
|
|
449
|
+
fs.writeFileSync(gitignorePath, content.replace(/\n*$/, '\n') + entry, 'utf8');
|
|
412
450
|
console.log(` .gitignore → appended ${docsDir}/ entry`);
|
|
413
451
|
}
|
|
414
452
|
/** AI config files that init may have created, for all tools or only the given tool ids. */
|
|
@@ -472,15 +510,25 @@ function removeAiConfig(filePath, file, docsDir, targetRoot, dryRun) {
|
|
|
472
510
|
console.log(` ${rel} → removed Switch section`);
|
|
473
511
|
}
|
|
474
512
|
}
|
|
513
|
+
function escapeRegExp(s) {
|
|
514
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Drop the `docsDir/` line init added, together with the comment line directly above it.
|
|
518
|
+
* The comment and the path are matched as one unit so that a project carrying entries for
|
|
519
|
+
* two docs folders (one left by an older version, one current) keeps the comment belonging
|
|
520
|
+
* to the entry that stays.
|
|
521
|
+
*/
|
|
522
|
+
function stripGitignoreEntry(content, docsDir) {
|
|
523
|
+
const pattern = new RegExp(`(\n# switch-scripting-context \\(AI context docs[^\n]*\\))?\n${escapeRegExp(docsDir)}/\n`, 'g');
|
|
524
|
+
return content.replace(pattern, '\n');
|
|
525
|
+
}
|
|
475
526
|
function removeGitignoreEntry(targetRoot, docsDir, dryRun) {
|
|
476
527
|
const gitignorePath = path.join(targetRoot, '.gitignore');
|
|
477
528
|
if (!fs.existsSync(gitignorePath))
|
|
478
529
|
return;
|
|
479
530
|
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
480
|
-
|
|
481
|
-
const updated = content
|
|
482
|
-
.replace(/\n# switch-scripting-context \(AI context docs[^\n]*\)\n/g, '\n')
|
|
483
|
-
.replace(new RegExp(`\n${docsDir}/\n`, 'g'), '\n');
|
|
531
|
+
const updated = stripGitignoreEntry(content, docsDir);
|
|
484
532
|
if (updated === content)
|
|
485
533
|
return;
|
|
486
534
|
if (dryRun) {
|
|
@@ -521,6 +569,7 @@ function remove(options) {
|
|
|
521
569
|
else {
|
|
522
570
|
console.log(` ${options.docsDir}/ → not found, skipping`);
|
|
523
571
|
}
|
|
572
|
+
removeLegacyDocsDir(targetRoot, options.docsDir, options.dryRun);
|
|
524
573
|
// 2. Remove AI config sections / files
|
|
525
574
|
console.log('\nCleaning AI config files...');
|
|
526
575
|
for (const { filePath, file } of aiConfigFiles(targetRoot, options.docsDir)) {
|
|
@@ -540,6 +589,7 @@ function init(options) {
|
|
|
540
589
|
console.log('\nDry run — no files will be written.\n');
|
|
541
590
|
// 1. Copy docs
|
|
542
591
|
console.log('Copying docs...');
|
|
592
|
+
removeLegacyDocsDir(targetRoot, options.docsDir, options.dryRun);
|
|
543
593
|
copyDocs(packageRoot, docsDestDir, options.dryRun);
|
|
544
594
|
stampDocsVersion(packageRoot, docsDestDir, options.dryRun);
|
|
545
595
|
// 2. Generate AI config files
|
|
@@ -573,12 +623,12 @@ function parseArgs(argv) {
|
|
|
573
623
|
let toolsExplicit = false;
|
|
574
624
|
const initOptions = {
|
|
575
625
|
tools: TOOL_REGISTRY.map(t => t.id),
|
|
576
|
-
docsDir: '
|
|
626
|
+
docsDir: 'docs-for-agents',
|
|
577
627
|
force: false,
|
|
578
628
|
dryRun: false,
|
|
579
629
|
};
|
|
580
630
|
const removeOptions = {
|
|
581
|
-
docsDir: '
|
|
631
|
+
docsDir: 'docs-for-agents',
|
|
582
632
|
dryRun: false,
|
|
583
633
|
};
|
|
584
634
|
for (let i = 1; i < args.length; i++) {
|
|
@@ -658,7 +708,7 @@ Options (init):
|
|
|
658
708
|
IDs: claude, copilot, cursor, codex, gemini, windsurf, zed, cline
|
|
659
709
|
Aliases: opencode (→ codex), clawcode (→ claude)
|
|
660
710
|
Default: all tools
|
|
661
|
-
--docs-dir <dir> Destination folder for docs in target project (default:
|
|
711
|
+
--docs-dir <dir> Destination folder for docs in target project (default: docs-for-agents)
|
|
662
712
|
--force Overwrite existing AI config files rather than merging
|
|
663
713
|
--dry-run Print what would happen without writing any files
|
|
664
714
|
--help Show this help message
|
|
@@ -666,7 +716,7 @@ Options (init):
|
|
|
666
716
|
Options (remove):
|
|
667
717
|
--tools <list> Only remove these tools' config; keeps the docs folder and other tools
|
|
668
718
|
Default: remove everything, same as listing every tool
|
|
669
|
-
--docs-dir <dir> Docs folder to remove (default:
|
|
719
|
+
--docs-dir <dir> Docs folder to remove (default: docs-for-agents)
|
|
670
720
|
--dry-run Print what would happen without writing any files
|
|
671
721
|
|
|
672
722
|
Examples:
|
|
@@ -24,6 +24,13 @@ and how often to log is a deliberate choice, not a default.
|
|
|
24
24
|
| `Info` | Normal, expected checkpoints worth surfacing to a flow operator without them needing debug mode (e.g. "processed 40 records", "output written to X"). |
|
|
25
25
|
| `Debug` | Verbose detail for diagnosing execution. Many users leave debug logging enabled in production to self-diagnose issues and to help app developers and Enfocus Support — leave meaningful checkpoints in, but trim noisy or no-longer-useful debug logs before shipping. **`Debug` messages only reach the log database if the Switch preference "Log debug messages" is turned on** (off by default) — see [logs-and-dataroot.md](../switch-project/logs-and-dataroot.md#retention-and-the-debug-level-gate). |
|
|
26
26
|
|
|
27
|
+
## Don't add a verbosity property
|
|
28
|
+
|
|
29
|
+
Switch already has the switch: `Debug` messages only reach the log database when the preference
|
|
30
|
+
"Log debug messages" is on. A script-level "log detail" or "verbose logging" property duplicates a
|
|
31
|
+
control the user already has, adds a row to the property pane, and (for an app) adds strings to
|
|
32
|
+
translate. Put verbose detail behind `LogLevel.Debug` and let the preference govern it.
|
|
33
|
+
|
|
27
34
|
## Logging in loops
|
|
28
35
|
|
|
29
36
|
Don't categorically avoid logging inside loops — per-item logs can matter (e.g. a per-job error).
|
|
@@ -162,11 +162,15 @@ Unlike the sections above, these are documented in full here rather than deferri
|
|
|
162
162
|
approval. Note that an unsigned app always loads under the **Custom** subcategory regardless of
|
|
163
163
|
what it declares — the declared subcategory only takes effect once Enfocus has signed the app, and
|
|
164
164
|
review may change it.
|
|
165
|
-
- [ ] **`DetailedInfo` is
|
|
166
|
-
Switch Designer can export a *flow's* HTML
|
|
167
|
-
|
|
165
|
+
- [ ] **`DetailedInfo` is written wherever it adds something a `Tooltip` can't carry** — not
|
|
166
|
+
mechanically on every property. It is optional: Switch Designer can export a *flow's* HTML
|
|
167
|
+
documentation from every element in it, so an empty `DetailedInfo` produces a blank entry there
|
|
168
|
+
if a flow builder ever generates one, which is a reason to fill it in where the property warrants
|
|
169
|
+
it, not a reason to pad out every trivial one. See
|
|
168
170
|
[property-documentation.md](../switch-project/property-documentation.md) for what to write in it, and when
|
|
169
|
-
it's fine to leave blank.
|
|
171
|
+
it's fine to leave blank. The [app manual](app-manual.md#flow-elements-properties) is the surface
|
|
172
|
+
users are far more likely to read, and it needs real per-property content regardless of what
|
|
173
|
+
`DetailedInfo` holds.
|
|
170
174
|
- [ ] **`Description`, `Compatibility`, `SupportInfo` and `AppDiscovery` are filled in.** These are
|
|
171
175
|
required to create the `.enfpack` at all, and `SupportInfo` must point at *your* support channel,
|
|
172
176
|
never Enfocus Support — see [app-store-listing.md](app-store-listing.md) for what to write
|
|
@@ -158,9 +158,9 @@ offer it as the property's only editor — pair it with a module-free option (e.
|
|
|
158
158
|
|
|
159
159
|
| Property kind | Recommended `Editor` chain | Why |
|
|
160
160
|
|---|---|---|
|
|
161
|
-
| Secret (password, token, API key) | `
|
|
161
|
+
| Secret (password, token, API key) | `inline` only, with `Type="password"` | Masks the value and avoids leaking it via logs or variable inspection if a var/expression editor were combined. `password` is a `Type`, not an editor token — there is no `password` entry in the [modal editor](#modal-editors) list, so `Editor="password"` is wrong. |
|
|
162
162
|
| Scalar job-specific value (job ID, copy count, company name, date, etc.) | `inline` (`Type` matching the value) + `sltextwithvar` + `scriptexp` | Hard-coded default, plus dynamic substitution and full expression evaluation. |
|
|
163
|
-
| Array/list job-specific value | `stringlist` + `mltextwithvar` + `scriptexp` | One item per line
|
|
163
|
+
| Array/list job-specific value | `stringlist` + `mltextwithvar` + `scriptexp` | One item per line. **Caveat:** the intent is that every path returns a `string[]`, but the [modal editor table](#modal-editors) lists `mltextwithvar` as returning a single string, and which one holds for this chain is unverified. Handle both — split on newlines when a bare string comes back. |
|
|
164
164
|
| Structured blob text (XML/JSON/HTML, email body) | `description` + `mltextwithvar` + `scriptexp` | Free multi-line text suits a single blob of content better than a line-per-item list. |
|
|
165
165
|
| Boolean used as a `Dependency` master | `inline` only | Other properties' visibility depends on a value known at design time. |
|
|
166
166
|
| Boolean consumed directly by script logic (not a `Dependency` master) | `inline` + `conditionwithvar` + `scriptexp` | Safe to toggle dynamically since nothing else depends on it for visibility. Also covers booleans that are themselves a `Dependency` *dependent* (not a master). |
|
|
@@ -174,6 +174,50 @@ offer it as the property's only editor — pair it with a module-free option (e.
|
|
|
174
174
|
combined with `sltextwithvar`/`scriptexp` too, but this is uncommon in practice and not covered by a
|
|
175
175
|
dedicated row above.
|
|
176
176
|
|
|
177
|
+
### A property whose shape varies by a type selector
|
|
178
|
+
|
|
179
|
+
When what a property must hold depends on another property's value — a map for one type, an
|
|
180
|
+
ordered list for another, a pair of labelled strings for a third — don't reach for one property
|
|
181
|
+
holding JSON or a hand-rolled line format. Declare an enum master and one dependent per shape, each
|
|
182
|
+
with the editor that natively fits it, and read only the dependent the master currently shows.
|
|
183
|
+
|
|
184
|
+
The shapes then need no parsing at all in the common cases, and the property pane shows one
|
|
185
|
+
type-appropriate editor with a label that explains itself instead of a text box whose format lives
|
|
186
|
+
in the documentation:
|
|
187
|
+
|
|
188
|
+
```xml
|
|
189
|
+
<!-- master: enum, inline only, so the value is known at flow-configuration time -->
|
|
190
|
+
<QType UserDefined="true" Type="enum:(not used);List;Ordered;Pair" Editor="inline" Subtype="inline"
|
|
191
|
+
LocalizedTagName="Type" Tooltip="" DetailedInfo="" Validation="Standard"
|
|
192
|
+
Default="(not used)">(not used)</QType>
|
|
193
|
+
|
|
194
|
+
<!-- one dependent per shape; each is hidden unless its condition holds -->
|
|
195
|
+
<QList UserDefined="true" Type="stringlist" Editor="stringlist" Subtype="stringlist"
|
|
196
|
+
Dependency="QType" DependencyCondition="Equals" Dependencyvalue="List"
|
|
197
|
+
LocalizedTagName="Items" Tooltip="" DetailedInfo="" Validation="Standard"></QList>
|
|
198
|
+
<QLow UserDefined="true" Type="string" Editor="description" Subtype=""
|
|
199
|
+
Dependency="QType" DependencyCondition="Equals" Dependencyvalue="Pair"
|
|
200
|
+
LocalizedTagName="What a low value means" Tooltip="" DetailedInfo=""
|
|
201
|
+
Validation="Standard" Default=""></QLow>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Adding `(not used)` to the master's items makes it double as the group's on/off switch, so the
|
|
205
|
+
group needs no separate boolean.
|
|
206
|
+
|
|
207
|
+
Two constraints come with the pattern:
|
|
208
|
+
|
|
209
|
+
- The master must be an enum or a boolean with `inline` only, per the rows above. A dynamic master
|
|
210
|
+
makes the visible set differ per job, which the dependents' own editors cannot account for.
|
|
211
|
+
- Reading a hidden dependent throws `"Invalid tag: <tag>"`. Check the master's value, or call
|
|
212
|
+
`hasProperty(tag)`, before every read — see
|
|
213
|
+
[script-declaration.md § Dependency](script-declaration.md#custom-property-elements-elementfields).
|
|
214
|
+
This bites hardest in a loop over jobs or connections, where the throw repeats on every
|
|
215
|
+
invocation.
|
|
216
|
+
|
|
217
|
+
A shape the master cannot enumerate, or one that must vary per job, is the case this pattern does
|
|
218
|
+
not cover; that is where a single `description` property holding a structured blob is the honest
|
|
219
|
+
answer.
|
|
220
|
+
|
|
177
221
|
---
|
|
178
222
|
|
|
179
223
|
## Notes
|