@kubohiroya/turbowarp-extension-data-editor 0.8.0 → 0.8.2

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.
Files changed (38) hide show
  1. package/README.ja.md +2 -1
  2. package/README.md +2 -1
  3. package/dist/build-info.json +3 -3
  4. package/dist/form-presentation.d.ts +1 -1
  5. package/dist/form-presentation.js +2 -0
  6. package/dist/react.js +3 -0
  7. package/dist/turbowarp/appearance.d.ts +7 -6
  8. package/dist/turbowarp/bridge.d.ts +14 -19
  9. package/dist/turbowarp/bridge.js +21 -16
  10. package/dist/turbowarp/error-session.d.ts +3 -17
  11. package/dist/turbowarp/error-session.js +2 -2
  12. package/dist/turbowarp/fixed-fields.d.ts +62 -77
  13. package/dist/turbowarp/fixed-fields.js +6 -6
  14. package/dist/turbowarp/fixed-transaction.d.ts +23 -18
  15. package/dist/turbowarp/fixed-transaction.js +13 -13
  16. package/dist/turbowarp/host.d.ts +2 -18
  17. package/dist/turbowarp/host.js +9 -6
  18. package/dist/turbowarp/icon.d.ts +1 -8
  19. package/dist/turbowarp/icon.js +6 -6
  20. package/dist/turbowarp/native-types.d.ts +136 -0
  21. package/dist/turbowarp/native-types.js +1 -0
  22. package/dist/turbowarp/subtree.d.ts +12 -9
  23. package/dist/turbowarp/subtree.js +3 -2
  24. package/dist/webpack4/metafile.json +22 -22
  25. package/dist/webpack4/react.js +1 -0
  26. package/dist/webpack4/react.js.map +2 -2
  27. package/dist/webpack4/rjsf.js +2 -0
  28. package/dist/webpack4/rjsf.js.map +2 -2
  29. package/dist/webpack4/turbowarp.js +36 -29
  30. package/dist/webpack4/turbowarp.js.map +2 -2
  31. package/docs/ci-success-gate.md +2 -2
  32. package/docs/common-layout.ja.md +4 -4
  33. package/docs/descendants.md +2 -2
  34. package/docs/dialog-window.md +2 -2
  35. package/docs/schema-stage1.ja.md +3 -3
  36. package/docs/template-fixture-separation.ja.md +5 -5
  37. package/package.json +7 -5
  38. package/webpack4.cjs +19 -20
package/README.ja.md CHANGED
@@ -109,6 +109,7 @@ pnpm test
109
109
  ```
110
110
 
111
111
  `pnpm test` は型宣言を含むビルド後、コアと標準フォームのテストを実行します。
112
+ `pnpm run test:runtime` は、最小 editor runtime(`@kubohiroya/turbowarp-plus-editor-test-runtime`)の上で、TurboWarp Plus Editor が固定する VM と Scratch Blocks を使い、Editor をビルドせずにエディターを Chromium で試します。試験用の拡張機能が固定入力のエディターを登録し、ブロックのアイコンからダイアログを開いて、検証・保存・本物のドロップダウンの操作を確かめます。先に Playwright の Chromium を入れてください(`pnpm exec playwright install chromium`)。
112
113
  パッケージは ESM です。ブラウザには ES2022 の組み込み API が必要です。
113
114
  標準フォームの React / ReactDOM peer は 16.14 / 17 / 18。GUI は既存の 16.14 を共有します。
114
115
  初回の実験版は `@kubohiroya/turbowarp-extension-data-editor@0.1.0` です。固定SchemaのSource/Form編集は `0.2.0`、既定OFFの固定入力契約・標準field transaction・固定フォームとGUI接続は `0.2.1` で追加しました。配布版 `0.2.2` は、編集見出しのブロック名・アイコン、項目の削除メニュー・追加ボタン、key-valueの2行表示を含みます。
@@ -153,7 +154,7 @@ npm install --save-exact @kubohiroya/turbowarp-extension-data-editor@0.2.2
153
154
 
154
155
  ## Optional editing examples
155
156
 
156
- Repository examples are opt-in teaching code, not template blocks or additional package exports. See [the JSON/YAML document example](examples/document-properties.mjs) and [its guide](examples/README.ja.md). Each extension owns its actual data contract.
157
+ Repository examples are opt-in teaching code, not template blocks or additional package exports. See [the JSON/YAML document example](examples/document-properties.ts) and [its guide](examples/README.ja.md). Each extension owns its actual data contract.
157
158
 
158
159
  Prepared source editing is an optional host capability. See [the source/form contract](docs/source-forms.ja.md) for fixed-schema bindings, atomic saves, compatibility, and rollback.
159
160
 
package/README.md CHANGED
@@ -107,6 +107,7 @@ pnpm test
107
107
  ```
108
108
 
109
109
  `pnpm test` builds the package, including type declarations, then runs the core and standard form tests.
110
+ `pnpm run test:runtime` runs the editors on the TurboWarp Plus Editor's pinned VM and Scratch Blocks in Chromium, through the minimal editor runtime (`@kubohiroya/turbowarp-plus-editor-test-runtime`), without building the Editor: a fixture extension registers a fixed input editor, and the tests open its dialog from the block's icon, validate, save and use the native dropdown. Install Chromium for Playwright first (`pnpm exec playwright install chromium`).
110
111
  The package uses ESM. Browsers need ES2022 built-in APIs.
111
112
  The standard form supports React / ReactDOM peers 16.14, 17, and 18. The GUI shares its existing React 16.14 installation.
112
113
  The first experimental release was `@kubohiroya/turbowarp-extension-data-editor@0.1.0`. Fixed-schema Source/Form editing was added in `0.2.0`; default-off fixed-input contracts, standard field transactions, fixed-input forms, and GUI integration followed in `0.2.1`. Release `0.2.2` includes block names and icons in editor headings, field removal menus and add buttons, and two-line key-value layouts.
@@ -151,7 +152,7 @@ The cross-repository validation record collected when introducing the existing S
151
152
 
152
153
  ## Optional editing examples
153
154
 
154
- Repository examples are opt-in teaching code, not template blocks or additional package exports. See [the JSON/YAML document example](examples/document-properties.mjs) and [its guide (Japanese)](examples/README.ja.md). Each extension owns its actual data contract.
155
+ Repository examples are opt-in teaching code, not template blocks or additional package exports. See [the JSON/YAML document example](examples/document-properties.ts) and [its guide (Japanese)](examples/README.ja.md). Each extension owns its actual data contract.
155
156
 
156
157
  Prepared source editing is an optional host capability. See [the source/form contract (Japanese)](docs/source-forms.ja.md) for fixed-schema bindings, atomic saves, compatibility, and rollback.
157
158
 
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@kubohiroya/turbowarp-extension-data-editor",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "repository": "https://github.com/kubohiroya/turbowarp-extension-data-editor",
5
- "revision": "5b8c653c6536518cf87c8b1273f73ff9eb47e1ca",
5
+ "revision": "28d7ca791fb696007ed014ebf3c2d28ba389a33a",
6
6
  "sourceState": "commit",
7
- "sourceSha256": "03d7dca0e78358e05dc242f769d71a4e90c0c49fdb7fa6b6f7196dd4281dcca2",
7
+ "sourceSha256": "fa5c13abb9fef48ea6c0e79eb01bbedc894927d2ef512fd33c9317d21a5027d6",
8
8
  "capabilities": {
9
9
  "preparedSource": 1
10
10
  }
@@ -5,4 +5,4 @@ export declare function FormHelp({ description, id }: {
5
5
  description: unknown;
6
6
  id?: string;
7
7
  }): React.ReactElement | null;
8
- export declare const formStyles = "\n[data-editor-form]{--editor-accent:var(--block-editor-accent,#426ad6);--editor-line:var(--block-editor-border,#dde3ed);--editor-muted:var(--block-editor-muted,var(--block-editor-text,#637086));color:var(--block-editor-text,#25334a);font:14px/1.5 system-ui,sans-serif;min-width:0}\n[data-editor-form] *,[data-editor-form] *:before,[data-editor-form] *:after{box-sizing:border-box}\n[data-editor-form] fieldset{min-width:0}\n[data-editor-form] fieldset.editor-form-binding,[data-editor-form] .editor-form-section,[data-editor-form] .rjsf fieldset{padding:0;border:0;margin:0 0 18px}\n[data-editor-form] .editor-form-section>legend,[data-editor-form] .rjsf fieldset>legend{font-size:15px;font-weight:650;padding:0;margin:0 0 10px;color:inherit}\n[data-editor-form] .editor-form-field,[data-editor-form] .rjsf .form-group{margin:0 0 14px;min-width:0}\n[data-editor-form] .editor-form-field>label,[data-editor-form] .rjsf .control-label{display:block;font-size:13px;font-weight:600;margin:0 0 6px;color:inherit}\n[data-editor-form] .required{color:var(--editor-accent);margin-left:3px}\n[data-editor-form] input:not([type=checkbox]):not([type=radio]),[data-editor-form] select,[data-editor-form] textarea{display:block;width:100%;min-height:38px;margin:0;padding:8px 11px;border:1px solid var(--editor-line);border-radius:7px;background:var(--block-editor-input,white);color:inherit;font:inherit;box-shadow:0 1px 2px #172b4d05}\n[data-editor-form] textarea{min-height:76px;resize:vertical}\n[data-editor-form] input:focus-visible,[data-editor-form] select:focus-visible,[data-editor-form] textarea:focus-visible,[data-editor-form] button:focus-visible,[data-editor-form] summary:focus-visible{outline:2px solid var(--editor-accent);outline-offset:2px}\n[data-editor-form] input[readonly]{background:var(--block-editor-subtle,var(--block-editor-background,#f5f7fb));color:var(--editor-muted)}\n[data-editor-form] input[type=checkbox]{width:17px;height:17px;margin:0;accent-color:var(--editor-accent)}\n[data-editor-form] .editor-form-field>label.editor-form-toggle,[data-editor-form] .rjsf .checkbox>label{display:flex;align-items:center;gap:8px;font-weight:500;margin:8px 0}\n[data-editor-form] button{font:inherit;cursor:pointer;border:1px solid var(--editor-line);border-radius:6px;background:var(--block-editor-input,white);color:inherit;padding:6px 10px;margin:0}\n[data-editor-form] button:disabled{cursor:default;opacity:.5}\n[data-editor-form] .editor-form-hint,[data-editor-form] .editor-form-help{color:var(--editor-muted);font-size:12px;line-height:1.5;margin:5px 0 10px}\n[data-editor-form] .editor-form-help>summary{display:list-item;list-style:disclosure-closed inside;cursor:pointer;width:fit-content;font-weight:500}\n[data-editor-form] .editor-form-help[open]>summary{list-style-type:disclosure-open}\n[data-editor-form] .editor-form-help p{margin:8px 0}\n[data-editor-form] .block-editor-layout-tabs>[role=tablist]{display:flex;gap:4px;flex-wrap:wrap;padding:5px;background:var(--block-editor-subtle,var(--block-editor-background,#f1f4f9));border:1px solid var(--editor-line);border-radius:9px;margin:4px 0 20px}\n[data-editor-form] .block-editor-layout-tabs>[role=tablist]>button{border:0;border-radius:6px;background:transparent;color:var(--editor-muted);padding:8px 18px;margin:0;font-weight:550;text-decoration:none;min-width:76px}\n[data-editor-form] .block-editor-layout-tabs>[role=tablist]>[aria-selected=true]{background:var(--block-editor-input,white);color:var(--editor-accent);box-shadow:0 1px 4px #1d30431a}\n[data-editor-form] .editor-form-error-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;margin-left:7px;padding:0 5px;border-radius:10px;background:#fff0f0;color:#b53d45;font-size:11px}\n[data-editor-form] fieldset.block-editor-layout-group{border:1px solid var(--editor-line);border-radius:10px;padding:16px 18px 4px;margin:0 0 18px;background:var(--block-editor-background,white)}\n[data-editor-form] fieldset.block-editor-layout-group>legend{padding:0 6px;font-size:13px;font-weight:650}\n[data-editor-form] .block-editor-layout-grid{display:grid;gap:0 18px;grid-template-columns:repeat(var(--editor-columns),minmax(0,1fr));align-items:start}\n[data-editor-form] .block-editor-layout-grid>*{min-width:0}\n[data-editor-form] .block-editor-layout-tabs>[hidden]{display:none!important}\n[data-editor-form] .editor-form-toolbar{display:flex;align-items:start;justify-content:space-between;gap:12px;margin:0 0 18px;padding-bottom:14px;border-bottom:1px solid var(--editor-line)}\n[data-editor-form] .editor-form-modes{display:flex;gap:3px;border:1px solid var(--editor-line);border-radius:8px;padding:3px;background:var(--block-editor-subtle,var(--block-editor-background,#f1f4f9))}\n[data-editor-form] .editor-form-modes>button{border:0;border-radius:5px;padding:5px 14px;background:transparent;color:var(--editor-muted);margin:0}\n[data-editor-form] .editor-form-modes>[aria-pressed=true]{background:var(--block-editor-input,white);color:var(--editor-accent);box-shadow:0 1px 3px #1d30431a}\n[data-editor-form] .editor-form-tools{font-size:12px;color:var(--editor-muted);margin:0 0 12px}\n[data-editor-form] .editor-form-tools>summary{cursor:pointer;padding:5px 0;list-style-position:inside}\n[data-editor-form] .editor-form-tools[open]{padding:10px 12px;border:1px solid var(--editor-line);border-radius:8px;background:var(--block-editor-subtle,var(--block-editor-background,#f7f9fc))}\n[data-editor-form] .editor-form-tools button{border:1px solid var(--editor-line);border-radius:6px;background:var(--block-editor-input,white);padding:5px 9px;margin:5px 5px 5px 0;color:inherit}\n[data-editor-form] .editor-form-tools label{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:5px 0}\n[data-editor-form] .editor-form-invalid{font-size:12px;color:#b53d45;margin:5px 0}\n[data-editor-form] [data-source-error=true]>input,[data-editor-form] [data-source-error=true]>textarea{border-color:#b53d45}\n[data-editor-form] .editor-form-array-item{display:flex;align-items:start;gap:10px}\n[data-editor-form] .editor-form-array-item>.editor-form-field{flex:1;min-width:0}\n@media(max-width:600px){[data-editor-form] .block-editor-layout-grid{grid-template-columns:minmax(0,1fr)}[data-editor-form] fieldset.block-editor-layout-group{padding:12px 12px 0}[data-editor-form] .block-editor-layout-tabs>[role=tablist]>button{padding:7px 12px;min-width:60px}[data-editor-form] .editor-form-toolbar{flex-wrap:wrap}}\n";
8
+ export declare const formStyles = "\n[data-editor-form]{--editor-accent:var(--block-editor-accent,#426ad6);--editor-line:var(--block-editor-border,#dde3ed);--editor-muted:var(--block-editor-muted,var(--block-editor-text,#637086));color:var(--block-editor-text,#25334a);font:14px/1.5 system-ui,sans-serif;min-width:0}\n[data-editor-form] *,[data-editor-form] *:before,[data-editor-form] *:after{box-sizing:border-box}\n[data-editor-form] fieldset{min-width:0}\n[data-editor-form] fieldset.editor-form-binding,[data-editor-form] .editor-form-section,[data-editor-form] .rjsf fieldset{padding:0;border:0;margin:0 0 18px}\n[data-editor-form] .editor-form-section>legend,[data-editor-form] .rjsf fieldset>legend{font-size:15px;font-weight:650;padding:0;margin:0 0 10px;color:inherit}\n[data-editor-form] .editor-form-field,[data-editor-form] .rjsf .form-group{margin:0 0 14px;min-width:0}\n[data-editor-form] .editor-form-field>label,[data-editor-form] .rjsf .control-label{display:block;font-size:13px;font-weight:600;margin:0 0 6px;color:inherit}\n[data-editor-form] [data-block-editor-pair-row] label,[data-editor-form] .rjsf [data-block-editor-pair-row] .control-label{margin-bottom:0}\n[data-editor-form] .required{color:var(--editor-accent);margin-left:3px}\n[data-editor-form] input:not([type=checkbox]):not([type=radio]),[data-editor-form] select,[data-editor-form] textarea{display:block;width:100%;min-height:38px;margin:0;padding:8px 11px;border:1px solid var(--editor-line);border-radius:7px;background:var(--block-editor-input,white);color:inherit;font:inherit;box-shadow:0 1px 2px #172b4d05}\n[data-editor-form] textarea{min-height:76px;resize:vertical}\n[data-editor-form] input:not([type=checkbox]):not([type=radio])[data-block-editor-invalid=\"true\"],[data-editor-form] select[data-block-editor-invalid=\"true\"],[data-editor-form] textarea[data-block-editor-invalid=\"true\"]{border-color:var(--block-editor-error-border,#c62828);box-shadow:0 0 0 1px var(--block-editor-error-border,#c62828);background:linear-gradient(var(--block-editor-error-tint,rgba(198,40,40,0.08)),var(--block-editor-error-tint,rgba(198,40,40,0.08))),var(--block-editor-input,white)}\n[data-editor-form] input:focus-visible,[data-editor-form] select:focus-visible,[data-editor-form] textarea:focus-visible,[data-editor-form] button:focus-visible,[data-editor-form] summary:focus-visible{outline:2px solid var(--editor-accent);outline-offset:2px}\n[data-editor-form] input[readonly]{background:var(--block-editor-subtle,var(--block-editor-background,#f5f7fb));color:var(--editor-muted)}\n[data-editor-form] input[type=checkbox]{width:17px;height:17px;margin:0;accent-color:var(--editor-accent)}\n[data-editor-form] .editor-form-field>label.editor-form-toggle,[data-editor-form] .rjsf .checkbox>label{display:flex;align-items:center;gap:8px;font-weight:500;margin:8px 0}\n[data-editor-form] button{font:inherit;cursor:pointer;border:1px solid var(--editor-line);border-radius:6px;background:var(--block-editor-input,white);color:inherit;padding:6px 10px;margin:0}\n[data-editor-form] button:disabled{cursor:default;opacity:.5}\n[data-editor-form] .editor-form-hint,[data-editor-form] .editor-form-help{color:var(--editor-muted);font-size:12px;line-height:1.5;margin:5px 0 10px}\n[data-editor-form] .editor-form-help>summary{display:list-item;list-style:disclosure-closed inside;cursor:pointer;width:fit-content;font-weight:500}\n[data-editor-form] .editor-form-help[open]>summary{list-style-type:disclosure-open}\n[data-editor-form] .editor-form-help p{margin:8px 0}\n[data-editor-form] .block-editor-layout-tabs>[role=tablist]{display:flex;gap:4px;flex-wrap:wrap;padding:5px;background:var(--block-editor-subtle,var(--block-editor-background,#f1f4f9));border:1px solid var(--editor-line);border-radius:9px;margin:4px 0 20px}\n[data-editor-form] .block-editor-layout-tabs>[role=tablist]>button{border:0;border-radius:6px;background:transparent;color:var(--editor-muted);padding:8px 18px;margin:0;font-weight:550;text-decoration:none;min-width:76px}\n[data-editor-form] .block-editor-layout-tabs>[role=tablist]>[aria-selected=true]{background:var(--block-editor-input,white);color:var(--editor-accent);box-shadow:0 1px 4px #1d30431a}\n[data-editor-form] .editor-form-error-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;margin-left:7px;padding:0 5px;border-radius:10px;background:#fff0f0;color:#b53d45;font-size:11px}\n[data-editor-form] fieldset.block-editor-layout-group{border:1px solid var(--editor-line);border-radius:10px;padding:16px 18px 4px;margin:0 0 18px;background:var(--block-editor-background,white)}\n[data-editor-form] fieldset.block-editor-layout-group>legend{padding:0 6px;font-size:13px;font-weight:650}\n[data-editor-form] .block-editor-layout-grid{display:grid;gap:0 18px;grid-template-columns:repeat(var(--editor-columns),minmax(0,1fr));align-items:start}\n[data-editor-form] .block-editor-layout-grid>*{min-width:0}\n[data-editor-form] .block-editor-layout-tabs>[hidden]{display:none!important}\n[data-editor-form] .editor-form-toolbar{display:flex;align-items:start;justify-content:space-between;gap:12px;margin:0 0 18px;padding-bottom:14px;border-bottom:1px solid var(--editor-line)}\n[data-editor-form] .editor-form-modes{display:flex;gap:3px;border:1px solid var(--editor-line);border-radius:8px;padding:3px;background:var(--block-editor-subtle,var(--block-editor-background,#f1f4f9))}\n[data-editor-form] .editor-form-modes>button{border:0;border-radius:5px;padding:5px 14px;background:transparent;color:var(--editor-muted);margin:0}\n[data-editor-form] .editor-form-modes>[aria-pressed=true]{background:var(--block-editor-input,white);color:var(--editor-accent);box-shadow:0 1px 3px #1d30431a}\n[data-editor-form] .editor-form-tools{font-size:12px;color:var(--editor-muted);margin:0 0 12px}\n[data-editor-form] .editor-form-tools>summary{cursor:pointer;padding:5px 0;list-style-position:inside}\n[data-editor-form] .editor-form-tools[open]{padding:10px 12px;border:1px solid var(--editor-line);border-radius:8px;background:var(--block-editor-subtle,var(--block-editor-background,#f7f9fc))}\n[data-editor-form] .editor-form-tools button{border:1px solid var(--editor-line);border-radius:6px;background:var(--block-editor-input,white);padding:5px 9px;margin:5px 5px 5px 0;color:inherit}\n[data-editor-form] .editor-form-tools label{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:5px 0}\n[data-editor-form] .editor-form-invalid{font-size:12px;color:#b53d45;margin:5px 0}\n[data-editor-form] [data-source-error=true]>input,[data-editor-form] [data-source-error=true]>textarea{border-color:#b53d45}\n[data-editor-form] .editor-form-array-item{display:flex;align-items:start;gap:10px}\n[data-editor-form] .editor-form-array-item>.editor-form-field{flex:1;min-width:0}\n@media(max-width:600px){[data-editor-form] .block-editor-layout-grid{grid-template-columns:minmax(0,1fr)}[data-editor-form] fieldset.block-editor-layout-group{padding:12px 12px 0}[data-editor-form] .block-editor-layout-tabs>[role=tablist]>button{padding:7px 12px;min-width:60px}[data-editor-form] .editor-form-toolbar{flex-wrap:wrap}}\n";
@@ -23,9 +23,11 @@ export const formStyles = `
23
23
  [data-editor-form] .editor-form-section>legend,[data-editor-form] .rjsf fieldset>legend{font-size:15px;font-weight:650;padding:0;margin:0 0 10px;color:inherit}
24
24
  [data-editor-form] .editor-form-field,[data-editor-form] .rjsf .form-group{margin:0 0 14px;min-width:0}
25
25
  [data-editor-form] .editor-form-field>label,[data-editor-form] .rjsf .control-label{display:block;font-size:13px;font-weight:600;margin:0 0 6px;color:inherit}
26
+ [data-editor-form] [data-block-editor-pair-row] label,[data-editor-form] .rjsf [data-block-editor-pair-row] .control-label{margin-bottom:0}
26
27
  [data-editor-form] .required{color:var(--editor-accent);margin-left:3px}
27
28
  [data-editor-form] input:not([type=checkbox]):not([type=radio]),[data-editor-form] select,[data-editor-form] textarea{display:block;width:100%;min-height:38px;margin:0;padding:8px 11px;border:1px solid var(--editor-line);border-radius:7px;background:var(--block-editor-input,white);color:inherit;font:inherit;box-shadow:0 1px 2px #172b4d05}
28
29
  [data-editor-form] textarea{min-height:76px;resize:vertical}
30
+ [data-editor-form] input:not([type=checkbox]):not([type=radio])[data-block-editor-invalid="true"],[data-editor-form] select[data-block-editor-invalid="true"],[data-editor-form] textarea[data-block-editor-invalid="true"]{border-color:var(--block-editor-error-border,#c62828);box-shadow:0 0 0 1px var(--block-editor-error-border,#c62828);background:linear-gradient(var(--block-editor-error-tint,rgba(198,40,40,0.08)),var(--block-editor-error-tint,rgba(198,40,40,0.08))),var(--block-editor-input,white)}
29
31
  [data-editor-form] input:focus-visible,[data-editor-form] select:focus-visible,[data-editor-form] textarea:focus-visible,[data-editor-form] button:focus-visible,[data-editor-form] summary:focus-visible{outline:2px solid var(--editor-accent);outline-offset:2px}
30
32
  [data-editor-form] input[readonly]{background:var(--block-editor-subtle,var(--block-editor-background,#f5f7fb));color:var(--editor-muted)}
31
33
  [data-editor-form] input[type=checkbox]{width:17px;height:17px;margin:0;accent-color:var(--editor-accent)}
package/dist/react.js CHANGED
@@ -197,6 +197,9 @@ export class BlockEditorDialog extends React.Component {
197
197
  this.stopFieldErrors = bindBlockEditorFieldErrors(this.body);
198
198
  if (typeof this.props.session.getNodes !== 'function')
199
199
  this.unmountBody = this.props.session.mount(this.body);
200
+ // Mounting the body validates what it shows without notifying; read the result, or Save stays enabled
201
+ // for a form that opened invalid.
202
+ this.setState(this.props.session.getState());
200
203
  // The modal establishes its focus scope after creating the portal.
201
204
  this.focusFrame = requestAnimationFrame(() => {
202
205
  if (!this.mounted)
@@ -1,7 +1,8 @@
1
- export default readBlockEditorAppearance;
2
- declare function readBlockEditorAppearance(block: any, ScratchBlocks: any, editableArguments: any): {
3
- name: any;
4
- colour: string;
5
- tint: string;
6
- iconURI: string;
1
+ import type { NativeBlock, NativeScratchBlocks } from './native-types.js';
2
+ declare const readBlockEditorAppearance: (block: NativeBlock | null, ScratchBlocks: NativeScratchBlocks, editableArguments?: readonly string[]) => {
3
+ name?: string;
4
+ colour?: string;
5
+ tint?: string;
6
+ iconURI?: string;
7
7
  };
8
+ export default readBlockEditorAppearance;
@@ -1,22 +1,17 @@
1
- export default createBlockEditorBridge;
2
- declare function createBlockEditorBridge({ ScratchBlocks, workspace, vm, service, label, canOpen, onOpen, onError, descendants, fixedInputs }: {
3
- ScratchBlocks: any;
4
- workspace: any;
5
- vm: any;
6
- service: any;
7
- label: any;
8
- canOpen: any;
9
- onOpen: any;
10
- onError: any;
11
- descendants?: boolean | undefined;
12
- fixedInputs?: boolean | undefined;
13
- }): {
1
+ import type { TurboWarpBlockEditorOptions } from '../turbowarp.js';
2
+ import type { BlockEditorHost, BlockReference } from '../types.js';
3
+ import type { EditorRegistry } from '../registry.js';
4
+ declare const openBlockEditorSession: ({ registry, reference, host, descendants }: {
5
+ registry: EditorRegistry;
6
+ reference: BlockReference;
7
+ host: BlockEditorHost;
8
+ descendants: boolean;
9
+ }) => import("../descendants.js").DescendantSession | import("../session.js").EditorSession | undefined;
10
+ declare const createBlockEditorBridge: ({ ScratchBlocks: nativeBlocks, workspace: nativeWorkspace, vm: nativeVM, service, label, canOpen, onOpen, onError, descendants, fixedInputs }: TurboWarpBlockEditorOptions & {
11
+ onError(error: unknown): void;
12
+ }) => {
14
13
  refresh: () => void;
15
14
  dispose: () => void;
16
15
  };
17
- export function openBlockEditorSession({ registry, reference, host, descendants }: {
18
- registry: any;
19
- reference: any;
20
- host: any;
21
- descendants: any;
22
- }): any;
16
+ export default createBlockEditorBridge;
17
+ export { openBlockEditorSession };
@@ -17,7 +17,10 @@ const openBlockEditorSession = ({ registry, reference, host, descendants }) => {
17
17
  return descendants && supportsDescendantEditing(host) ?
18
18
  registry.openDescendants(reference, host) : registry.open(reference, host);
19
19
  };
20
- const createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label, canOpen, onOpen, onError, descendants = false, fixedInputs = false }) => {
20
+ const createBlockEditorBridge = ({ ScratchBlocks: nativeBlocks, workspace: nativeWorkspace, vm: nativeVM, service, label, canOpen, onOpen, onError, descendants = false, fixedInputs = false }) => {
21
+ const ScratchBlocks = nativeBlocks;
22
+ const workspace = nativeWorkspace;
23
+ const vm = nativeVM;
21
24
  const host = createBlockEditorHost({ ScratchBlocks,
22
25
  workspace,
23
26
  vm,
@@ -40,7 +43,7 @@ const createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label,
40
43
  }
41
44
  };
42
45
  let disposed = false;
43
- let timer = null;
46
+ let timer;
44
47
  let activeSession = null;
45
48
  let openingSnapshot = null;
46
49
  let openingFixedDeclaration = null;
@@ -49,7 +52,7 @@ const createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label,
49
52
  const session = activeSession;
50
53
  if (!session)
51
54
  return;
52
- if (typeof session.invalidate === 'function') {
55
+ if ('invalidate' in session && typeof session.invalidate === 'function') {
53
56
  session.invalidate();
54
57
  return;
55
58
  }
@@ -61,42 +64,43 @@ const createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label,
61
64
  stopWaiting = session.subscribe(() => {
62
65
  if (session.getState().status === 'saving')
63
66
  return;
64
- stopWaiting();
67
+ stopWaiting === null || stopWaiting === void 0 ? void 0 : stopWaiting();
65
68
  stopWaiting = null;
66
69
  session.dispose();
67
70
  });
68
71
  }
69
72
  };
70
73
  const checkSession = () => {
71
- var _a;
74
+ var _a, _b;
72
75
  // The host owns rollback. Its synchronous transaction must retain the draft on failure.
73
76
  if (host.isCommitting())
74
77
  return;
75
78
  if (!activeSession || !openingSnapshot ||
76
79
  !['editing', 'saving'].includes(activeSession.getState().status))
77
80
  return;
78
- const current = typeof activeSession.getNodes === 'function' ?
81
+ const current = 'getNodes' in activeSession ?
79
82
  host.descendants.readSubtree(activeSession.reference) : host.readBlock(activeSession.reference);
80
83
  if (openingFixedDeclaration) {
81
84
  // Same-target external edits conflict at save while the session retains its draft.
82
85
  // Project/target/registration replacement ends the session and drops that draft.
83
- if (!current || ((_a = current.fixedContext) === null || _a === void 0 ? void 0 : _a.generation) !== openingSnapshot.fixedContext.generation ||
84
- fixedDeclaration(service.registry, current) !== openingFixedDeclaration)
86
+ if (!current || (!('fixedContext' in current) || !('fixedContext' in openingSnapshot) ||
87
+ ((_a = current.fixedContext) === null || _a === void 0 ? void 0 : _a.generation) !== ((_b = openingSnapshot.fixedContext) === null || _b === void 0 ? void 0 : _b.generation)) ||
88
+ fixedDeclaration(service.registry, 'opcode' in current ? current : null) !== openingFixedDeclaration)
85
89
  endSession();
86
90
  return;
87
91
  }
88
92
  if (!current || JSON.stringify(current) !== JSON.stringify(openingSnapshot))
89
93
  endSession();
90
94
  };
91
- const available = block => {
95
+ const available = (block) => {
92
96
  if (disposed || !vm.editingTarget || !canOpen() || !service.registry.resolve(block.type))
93
97
  return false;
94
98
  const snapshot = host.readBlock({ targetId: vm.editingTarget.id, blockId: block.id });
95
99
  if (fixedDeclaration(service.registry, snapshot))
96
100
  return supportsFixedInputEditing(host);
97
- return snapshot && Object.values(snapshot.arguments).every(slot => slot.kind === 'literal');
101
+ return snapshot !== null && Object.values(snapshot.arguments).every(slot => slot.kind === 'literal');
98
102
  };
99
- const open = block => {
103
+ const open = (block) => {
100
104
  var _a, _b;
101
105
  clearHover();
102
106
  if (activeSession && ['editing', 'saving'].includes(activeSession.getState().status))
@@ -109,15 +113,15 @@ const createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label,
109
113
  if (session) {
110
114
  ScratchBlocks.hideChaff();
111
115
  activeSession = session;
112
- openingSnapshot = typeof session.getNodes === 'function' ?
116
+ openingSnapshot = 'getNodes' in session ?
113
117
  host.descendants.readSubtree(session.reference) : host.readBlock(session.reference);
114
- openingFixedDeclaration = fixedDeclaration(service.registry, openingSnapshot) || null;
118
+ openingFixedDeclaration = fixedDeclaration(service.registry, openingSnapshot && 'opcode' in openingSnapshot ? openingSnapshot : null) || null;
115
119
  const labels = {};
116
120
  const appearances = Object.create(null);
117
121
  const rootAppearance = readBlockEditorAppearance(block, ScratchBlocks, (_a = service.registry.resolve(block.type)) === null || _a === void 0 ? void 0 : _a.editableArguments);
118
122
  appearances[reference.blockId] = rootAppearance;
119
123
  labels[reference.blockId] = rootAppearance.name || block.type;
120
- if (typeof session.getNodes === 'function') {
124
+ if ('getNodes' in session) {
121
125
  for (const node of session.getNodes()) {
122
126
  const source = workspace.getBlockById(node.reference.blockId);
123
127
  const appearance = readBlockEditorAppearance(source, ScratchBlocks, (_b = service.registry.resolve(node.opcode)) === null || _b === void 0 ? void 0 : _b.editableArguments);
@@ -205,12 +209,13 @@ const createBlockEditorBridge = ({ ScratchBlocks, workspace, vm, service, label,
205
209
  const unregister = service.subscribe(() => {
206
210
  // Aggregate sessions observe relevant registry changes themselves.
207
211
  if (openingFixedDeclaration && activeSession) {
208
- if (service.registry.resolveFixed(openingSnapshot.opcode) !== openingFixedDeclaration) {
212
+ if (openingSnapshot && 'opcode' in openingSnapshot &&
213
+ service.registry.resolveFixed(openingSnapshot.opcode) !== openingFixedDeclaration) {
209
214
  host.invalidate();
210
215
  endSession();
211
216
  }
212
217
  }
213
- else if (!activeSession || typeof activeSession.getNodes !== 'function') {
218
+ else if (!activeSession || !('getNodes' in activeSession)) {
214
219
  host.invalidate();
215
220
  endSession();
216
221
  }
@@ -1,18 +1,4 @@
1
+ import type { BlockReference } from '../types.js';
2
+ import type { BlockEditorFailureSession } from '../turbowarp.js';
3
+ declare const createBlockEditorErrorSession: (reference: BlockReference, error: unknown) => BlockEditorFailureSession;
1
4
  export default createBlockEditorErrorSession;
2
- declare function createBlockEditorErrorSession(reference: any, error: any): Readonly<{
3
- reference: any;
4
- getState: () => {
5
- status: string;
6
- canSave: boolean;
7
- issues: never[];
8
- error: any;
9
- };
10
- subscribe: (listener: any) => () => boolean;
11
- mount: () => () => void;
12
- save: () => Promise<{
13
- status: string;
14
- error: any;
15
- }>;
16
- cancel: () => boolean;
17
- dispose: () => true;
18
- }>;
@@ -2,7 +2,7 @@
2
2
  const createBlockEditorErrorSession = (reference, error) => {
3
3
  let status = 'editing';
4
4
  const listeners = new Set();
5
- const finish = next => {
5
+ const finish = (next) => {
6
6
  if (status !== 'editing')
7
7
  return false;
8
8
  status = next;
@@ -14,7 +14,7 @@ const createBlockEditorErrorSession = (reference, error) => {
14
14
  return Object.freeze({
15
15
  reference,
16
16
  getState: () => ({ status, canSave: false, issues: [], error }),
17
- subscribe: listener => {
17
+ subscribe: (listener) => {
18
18
  listeners.add(listener);
19
19
  return () => listeners.delete(listener);
20
20
  },
@@ -1,82 +1,67 @@
1
+ import type { FixedInputMetadata } from '../fixed-input-types.js';
2
+ import type { ArgumentSlot } from '../types.js';
3
+ import type { Identify, NativeBlock, NativeField, NativeFieldClass, NativeScratchBlocks, NativeTarget, VMBlock } from './native-types.js';
4
+ export interface FieldBinding {
5
+ block: NativeBlock;
6
+ field: NativeField;
7
+ fieldName: string;
8
+ value: string;
9
+ vmBlock: VMBlock;
10
+ kind: 'text' | 'number' | 'menu' | 'color';
11
+ Class: NativeFieldClass;
12
+ prototype: object;
13
+ allowedValues: readonly string[] | null | undefined;
14
+ restoreValue: NativeField['setValue'];
15
+ rawState: PropertyState[];
16
+ restoreHandles(): void;
17
+ restoreDom(): void;
18
+ restoreState?: ReturnType<typeof colorState>;
19
+ }
20
+ interface Inspection {
21
+ slot: ArgumentSlot;
22
+ metadata: FixedInputMetadata;
23
+ signatureParts: unknown[];
24
+ binding?: FieldBinding;
25
+ }
26
+ type PropertyState = [name: string, descriptor: PropertyDescriptor | undefined];
1
27
  /** Freeze native method identities once when the host attaches, before opening editors. */
2
- export function initializeFixedFields(ScratchBlocks: any): void;
28
+ export declare function initializeFixedFields(ScratchBlocks: NativeScratchBlocks): void;
29
+ declare const colorState: (block: NativeBlock, field: NativeField) => {
30
+ colors: unknown[];
31
+ hsv: unknown[][];
32
+ blockProperties: PropertyState[];
33
+ };
3
34
  /** Native objects stay local; only primitive metadata is returned in snapshots. */
4
- export function inspectFixedArgument({ ScratchBlocks, block, vmBlock, target, argument, role, identify, extensionId }: {
5
- ScratchBlocks: any;
6
- block: any;
7
- vmBlock: any;
8
- target: any;
9
- argument: any;
10
- role: any;
11
- identify: any;
12
- extensionId: any;
13
- }): {
14
- slot: {
15
- kind: string;
16
- value: string | number | boolean;
17
- } | {
18
- kind: string;
19
- value?: never;
20
- };
21
- metadata: {
22
- role: any;
23
- source: any;
24
- identity: string;
25
- reason: any;
26
- };
27
- signatureParts: any[];
28
- } | {
29
- slot: {
30
- kind: string;
31
- value?: never;
32
- };
33
- metadata: {
34
- role: any;
35
- source: string;
36
- identity: string;
37
- field?: never;
38
- };
39
- signatureParts: any[];
40
- } | {
41
- signatureParts: any[];
42
- binding?: {
43
- restoreState?: {
44
- colors: any[];
45
- hsv: any[][];
46
- blockProperties: any;
47
- };
48
- block: any;
49
- field: any;
50
- fieldName: any;
51
- value: string;
52
- vmBlock: any;
53
- kind: string;
54
- Class: any;
55
- prototype: any;
56
- allowedValues: readonly string[] | null | undefined;
57
- restoreValue: any;
58
- rawState: any;
59
- restoreHandles: any;
60
- restoreDom: any;
61
- };
62
- slot: {
63
- kind: string;
64
- value: string | number;
65
- };
66
- metadata: {
67
- role: any;
68
- source: string;
69
- identity: string;
70
- field: {
71
- blockId: any;
72
- name: any;
73
- type: string | undefined;
74
- };
75
- };
76
- } | null;
35
+ export declare function inspectFixedArgument({ ScratchBlocks, block, vmBlock, target, argument, role, identify, extensionId }: {
36
+ ScratchBlocks: NativeScratchBlocks;
37
+ block: NativeBlock;
38
+ vmBlock: VMBlock;
39
+ target: NativeTarget;
40
+ argument: string;
41
+ role: FixedInputMetadata['role'];
42
+ identify: Identify;
43
+ extensionId: string;
44
+ }): Inspection | null;
77
45
  /** Validate the exact saved primitive; native setters must not silently normalize it. */
78
- export function prepareFixedChange(binding: any, value: any): any;
46
+ export declare function prepareFixedChange(binding: FieldBinding, value: unknown): {
47
+ next: string;
48
+ block: NativeBlock;
49
+ field: NativeField;
50
+ fieldName: string;
51
+ value: string;
52
+ vmBlock: VMBlock;
53
+ kind: "text" | "number" | "menu" | "color";
54
+ Class: NativeFieldClass;
55
+ prototype: object;
56
+ allowedValues: readonly string[] | null | undefined;
57
+ restoreValue: NativeField["setValue"];
58
+ rawState: PropertyState[];
59
+ restoreHandles(): void;
60
+ restoreDom(): void;
61
+ restoreState?: ReturnType<typeof colorState>;
62
+ };
79
63
  /** Capture a later external value without invoking replacement field methods. */
80
- export function captureFixedFieldState(binding: any): any;
64
+ export declare function captureFixedFieldState(binding: FieldBinding): FieldBinding;
81
65
  /** Restore raw text and the standard color field's additional block/UI state. */
82
- export function restoreFixedField(binding: any): void;
66
+ export declare function restoreFixedField(binding: FieldBinding): void;
67
+ export {};
@@ -3,7 +3,7 @@ const own = (object, key) => {
3
3
  const descriptor = object && Object.getOwnPropertyDescriptor(object, key);
4
4
  return descriptor && 'value' in descriptor ? descriptor.value : undefined;
5
5
  };
6
- const primitive = value => typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
6
+ const primitive = (value) => typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
7
7
  const token = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/u;
8
8
  // Inspect data properties without invoking custom field accessors or menu providers.
9
9
  const inherited = (object, name) => {
@@ -41,7 +41,7 @@ const safeField = (field, Class) => {
41
41
  if (currentPrototype !== entry.prototype)
42
42
  return false;
43
43
  for (const [name, expected] of entry.own) {
44
- const actual = Object.getOwnPropertyDescriptor(currentPrototype, name);
44
+ const actual = Object.getOwnPropertyDescriptor(entry.prototype, name);
45
45
  if (Boolean(actual) !== Boolean(expected) ||
46
46
  (actual && (!('value' in actual) || !expected || !('value' in expected) || actual.value !== expected.value)))
47
47
  return false;
@@ -71,7 +71,7 @@ const safeField = (field, Class) => {
71
71
  }
72
72
  return false;
73
73
  };
74
- const arrayData = value => {
74
+ const arrayData = (value) => {
75
75
  if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype)
76
76
  return null;
77
77
  const result = [];
@@ -83,7 +83,7 @@ const arrayData = value => {
83
83
  }
84
84
  return result;
85
85
  };
86
- const staticValues = field => {
86
+ const staticValues = (field) => {
87
87
  const items = arrayData(own(field, 'menuGenerator_'));
88
88
  if (!items || items.length === 0)
89
89
  return null;
@@ -156,7 +156,7 @@ export function inspectFixedArgument({ ScratchBlocks, block, vmBlock, target, ar
156
156
  return { slot: { kind: 'reporter' }, metadata: { role, source: 'reporter', identity: JSON.stringify(sourceIdentity) },
157
157
  signatureParts: sourceIdentity };
158
158
  }
159
- let owner = direct ? block : connected;
159
+ const owner = direct ? block : connected;
160
160
  if (!owner) {
161
161
  if (vmBlock.fields && owns(vmBlock.fields, argument))
162
162
  return null;
@@ -204,7 +204,7 @@ export function inspectFixedArgument({ ScratchBlocks, block, vmBlock, target, ar
204
204
  }
205
205
  if (names.length !== 1)
206
206
  return unavailable('unsupported', 'The menu shadow does not have one standard field.');
207
- [name] = names;
207
+ name = names[0];
208
208
  }
209
209
  else
210
210
  return unavailable('unsupported', 'The shadow field family is unsupported.');
@@ -1,18 +1,23 @@
1
- export default function createFixedInputPort({ ScratchBlocks, workspace, vm, registry, identify, getState, begin, markEvent, finish, onError, subscribeScope }: {
2
- ScratchBlocks: any;
3
- workspace: any;
4
- vm: any;
5
- registry: any;
6
- identify: any;
7
- getState: any;
8
- begin: any;
9
- markEvent: any;
10
- finish: any;
11
- onError: any;
12
- subscribeScope: any;
13
- }): Readonly<{
14
- capability: "fixed-inputs-v1";
15
- readBlock: (reference: any, declaration: any) => import("../fixed-input-types.js").FixedBlockSnapshot | null;
16
- commit: (request: any) => void;
17
- subscribeInvalidation: (reference: any, declaration: any, listener: any) => () => void;
18
- }>;
1
+ import type { FixedInputEditorHost } from '../fixed-input-types.js';
2
+ import type { EditorRegistry } from '../registry.js';
3
+ import type { Identify, NativeChange, NativeScratchBlocks, NativeVM, NativeWorkspace } from './native-types.js';
4
+ interface FixedPortOptions {
5
+ ScratchBlocks: NativeScratchBlocks;
6
+ workspace: NativeWorkspace;
7
+ vm: NativeVM;
8
+ registry: EditorRegistry;
9
+ identify: Identify;
10
+ getState(): {
11
+ disposed: boolean;
12
+ revision: number;
13
+ generation: number;
14
+ externalRevision: number;
15
+ };
16
+ begin(): void;
17
+ markEvent(event: NativeChange): void;
18
+ finish(succeeded: boolean): void;
19
+ onError(error: unknown): void;
20
+ subscribeScope(listener: () => void): () => void;
21
+ }
22
+ export default function createFixedInputPort({ ScratchBlocks, workspace, vm, registry, identify, getState, begin, markEvent, finish, onError, subscribeScope }: FixedPortOptions): FixedInputEditorHost;
23
+ export {};