` entries). Hunt for these under ANY variable name; a root that silently becomes `{}`/`null` puts the component into a dead offline mode.- Dotted-name dispatchers (`commands["Takeoffs.list"]`, `cmd('Pages.list')`) must split the pa and resolve `window[namespace][method]`.- Availability probes are unreliable: namespace globals are lazy proxies, so ANY method name — including typos and removed commands — is truthy and callable, and only rejects with "Unknown command" when invoked. Fix probe NAMES (e.g. `'listByPage'` → `'list'`), treat an "Unknown command" rejection as a typo, and never feature-probe by calling entity commands (a probing insert creates real records).- `zzTakeoff.darkMode`, `zzTakeoff.post/on`, and `window.zzTakeoff` presence checks still work ## 8. Insert behavior (usually no code change) Inserts honor an explicit `order` and default to the next slot under the parent (folders sortfirst). Typed non-folder takeoffs get an auto color unless `properties.color` is set.`DrawObjects.insert` defaults `layerId` to the current layer and it must match the linkedtakeoff's layer. `startTakeoff` with `properties` auto-assigns nothing — include`properties.color` or the drawing is invisible. Layers/Libraries/Lists `insert` accepts `{name}`or `{properties: {name: {value}}}`. ## 9. Discipline - Fix comments/docs that describe removed APIs, but leave user-visible strings unless misleading.- Do not touch identifiers that merely LOOK like the old API (a plugin's own `this.commands` array, an XLSX parser's `l.name`, `canvas.getContext('2d')`).- Preserve line endings (some files are CRLF) and minified formatting where present.- After editing, re-scan for: `.commands`, `Storage`/`PluginLocalStorage`, `listByPage`, `getChildren`/`getDescendants`/`getCutouts`, `clearSelectedDrawObjects`, positional `DrawObjects.list(x)`/`Lists.list(x)`, single-object `X.update({...Id...})`, capitalized `.button` comparisons, and flat `.name`/`.id` reads on layer/library/list records — including inside strings, probes, fallback chains, and comments."/> ` entries). Hunt for these under ANY variable name; a root that silently becomes `{}`/`null` puts the component into a dead offline mode.- Dotted-name dispatchers (`commands["Takeoffs.list"]`, `cmd('Pages.list')`) must split the pa and resolve `window[namespace][method]`.- Availability probes are unreliable: namespace globals are lazy proxies, so ANY method name — including typos and removed commands — is truthy and callable, and only rejects with "Unknown command" when invoked. Fix probe NAMES (e.g. `'listByPage'` → `'list'`), treat an "Unknown command" rejection as a typo, and never feature-probe by calling entity commands (a probing insert creates real records).- `zzTakeoff.darkMode`, `zzTakeoff.post/on`, and `window.zzTakeoff` presence checks still work ## 8. Insert behavior (usually no code change) Inserts honor an explicit `order` and default to the next slot under the parent (folders sortfirst). Typed non-folder takeoffs get an auto color unless `properties.color` is set.`DrawObjects.insert` defaults `layerId` to the current layer and it must match the linkedtakeoff's layer. `startTakeoff` with `properties` auto-assigns nothing — include`properties.color` or the drawing is invisible. Layers/Libraries/Lists `insert` accepts `{name}`or `{properties: {name: {value}}}`. ## 9. Discipline - Fix comments/docs that describe removed APIs, but leave user-visible strings unless misleading.- Do not touch identifiers that merely LOOK like the old API (a plugin's own `this.commands` array, an XLSX parser's `l.name`, `canvas.getContext('2d')`).- Preserve line endings (some files are CRLF) and minified formatting where present.- After editing, re-scan for: `.commands`, `Storage`/`PluginLocalStorage`, `listByPage`, `getChildren`/`getDescendants`/`getCutouts`, `clearSelectedDrawObjects`, positional `DrawObjects.list(x)`/`Lists.list(x)`, single-object `X.update({...Id...})`, capitalized `.button` comparisons, and flat `.name`/`.id` reads on layer/library/list records — including inside strings, probes, fallback chains, and comments."/>
Back to zzTakeoff Community Channel LogoDocumentation
1h 56m

Scripting & UI Components Migration Prompt - TEST Server

Prompt to Migrate Scripts & UI Components:

# Migrate zzTakeoff plugin code to the new Plugin API


You are given zzTakeoff plugin artifacts: **scripts** and/or **components**. Rewrite them from the old

plugin API to the new one using the rules below. Make minimal, surgical edits — do not

restructure, reformat, or "improve" unrelated code. All changed code must still parse.


## 1. Entity `update` commands: single config object → positional `(id, updates)`


Old form passed one object containing the ID; new form takes the ID first, then an updates object:

- `Takeoffs.update({takeoffId, ...})` → `Takeoffs.update(takeoffId, {properties, parentId, order, toolMode})`

- `DrawObjects.update({drawObjectId, ...})` → `DrawObjects.update(drawObjectId, {properties, points, parentId})`

- `Bookmarks.update({bookmarkId, ...})` → `Bookmarks.update(bookmarkId, {name, pageId, parentId, order, properties})` (`pageId` is a new shortcut for the page property)

- `Templates.update({templateId, ...})` → `Templates.update(templateId, {properties, parentId, order, toolMode})`

- `Layers.update({layerId, name})` → `Layers.update(layerId, {name})`; same pattern for

`Libraries.update(libraryId, {name})` and `Lists.update(listId, {name})` (each also accepts `{properties: {name: {value}}}`)

- `Records.update({recordId, listId, set, unset})` → `Records.update(recordId, {type, order, parentId,

properties, applyProperties, toolMode, kind, listId})` — mongo-style set/unset is GONE. Put fields

directly in updates; a field set to `null` unsets it (`parentId: null` = move to root,

`properties: null` = clear all). `properties`/`applyProperties` are now MERGED with the existing

record, not replaced. `listId` inside updates is routing scope only (defaults to the current list).


Create-only fields must be REMOVED from update payloads (they are ignored or invalid now):

`kind`, `type`, `templateId`, `layerId` on takeoffs; `type`, `pageId`, `takeoffId`, `layerId` on

draw objects. To move a record to another layer, re-create it with the target `layerId` and delete

the original. `parentId` accepts `null` or `""` for root everywhere. Properties are merged by key

on all updates; pass a property as `null` to clear it.


## 2. Traversal commands replaced


- `X.getChildren(parentId)` is REMOVED for Bookmarks/Takeoffs/Templates → `X.list({parentId})`

(root children: `{parentId: ""}` — null, "", and missing all match each other). Also update any

feature probes (`typeof X.getChildren === "function"`) and comments that mention it.

- `X.getDescendants(id, {includeRecord})` → renamed `X.listDescendants(id, {includeRecord})`, same args.


## 3. `list` commands take one optional Mongo-style equality filter object


Primitive values only; dot paths reach nested values (`{type: "area", "properties.name.value": "Slab"}`).

Results are consistently sorted. Specific migrations:

- `DrawObjects.list(takeoffId)` (positional) → `DrawObjects.list({takeoffId})`

- `DrawObjects.listByPage(pageId)` is REMOVED → `DrawObjects.list({pageId})`. It no longer def

to the current page — pass the page ID explicitly. Also fix listByPage referenced via STRING

(e.g. `cmd('DrawObjects.listByPage', ...)`, name arrays, capability probes) and remove

fallback retry branches that attempt the old method in object and positional forms.

- `Lists.list('product')` → `Lists.list({type: 'product'})`

- `DrawObjects.getCutouts(id)` is REMOVED — cutouts are embedded on the parent draw object's

`cutouts` array, and the parent's measurements already subtract holes. Unfiltered

`DrawObjects.list()` includes annotations, cutout records, and system objects.

- Renamed: `DrawObjects.clearSelectedDrawObjects()` → `DrawObjects.clearSelected()` (fix probe

strings too). New: `Layers.get(layerId)`.


## 4. Return shapes: flat `name` promoted into `properties`


Layers, Libraries, and Lists now return `{_id, ..., properties: {name: {value, result, formatt

instead of a flat `name` (layers also return the full record instead of `{_id, name, projectId

Fix EVERY read of `.name` on records returned by Layers/Libraries/Lists commands — under ANY

variable name (`l.name`, `lib.name`, `active.name`, `layers[i].name`, map/label/log expressions) —

to `x.properties.name.value` (defensively: `(x.properties && x.properties.name && x.properties

Watch for stale `.id` reads too — records use `_id`. Bookmark results no longer include the page

under properties — read the root `pageId`. Do NOT touch `.name` on a plugin's own internal objects;

only records that came from these commands.


## 5. Dialogs


`promptPopover` now returns the clicked button's name lower/camel-cased: `"OK"` → `'ok'`,

`"Generate"` → `'generate'`, `"Save As"` → `'saveAs'`. A button named Cancel, or dismissing,

resolves `undefined`. Fix every comparison against capitalized button names

(`resp.button !== 'Generate'` → `'generate'`). A pattern like `if (!r || r.button === "Cancel")`

is safe (the `!r` guard covers cancel) — leave it. Input `type` names are space-separated word

(`"text multi line"`), `date` returns an ISO string, `radio` is a boolean toggle not a choice group.


## 6. Storage: `Storage` and `PluginLocalStorage` namespaces DO NOT EXIST (and never did)


The only storage namespaces are `PluginLocalSettings`, `PluginUserSettings`,

`PluginWorkspaceSettings` (key-value: positional `get(key)`, `set(key, value)`, `remove(key)`,

`keys()`, `clear()`), and `PluginLocalDB` (document DB). Replace every use of `Storage.*` or

`PluginLocalStorage.*` — including guarded probes (`typeof Storage !== "undefined"`,

`commands.Storage`, fallback chains like `PluginLocalStorage || PluginUserSettings || Storage`

with the appropriate real namespace (default `PluginLocalSettings` for device-local state;

`PluginUserSettings`/`PluginWorkspaceSettings` for synced state). Object-form calls

(`set({key, value})`) must become positional (`set(key, value)`). In components these are async

(await them); in scripts call them without await. Note `window.Storage` in an iframe is the

browser's native constructor — selecting it as a storage API is always a bug.


## 7. Components only: `zzTakeoff.commands` is GONE


`window.zzTakeoff` now carries only `pluginComponentKey`, `darkMode`, `post`, and `on`. Every

command namespace is a window global: `Pages`, `Projects`, `Layers`, `Bookmarks`, `Takeoffs`,

`DrawObjects`, `Libraries`, `Templates`, `Lists`, `Records`, `Reports`, `Files`, `XML`, `Tools

`Dialog`, `PluginLocalSettings`, `PluginUserSettings`, `PluginWorkspaceSettings`, `PluginLocalDB` —

plus the `Files`/`XML`/`Tools`/`Dialog` functions bound bare (`downloadFile`, `promptPopover`,

`notifySuccess`, `getContext`, `saveAll`, …). All calls are async.


Migrations:

- Delete destructures like `const {Pages} = zzTakeoff.commands` (the bare names already exist).

- Any aliased command root — `zz.commands`, `z.commands`, `host.commands`, `bridge.commands`,

`sn?.commands ?? {}`, `(b && b.commands) ? b.commands : null`, etc. — must resolve to the window

globals instead (e.g. `zz ? window : null`, or a curated object of the needed

`window.<Namespace>` entries). Hunt for these under ANY variable name; a root that silently

becomes `{}`/`null` puts the component into a dead offline mode.

- Dotted-name dispatchers (`commands["Takeoffs.list"]`, `cmd('Pages.list')`) must split the pa

and resolve `window[namespace][method]`.

- Availability probes are unreliable: namespace globals are lazy proxies, so ANY method name —

including typos and removed commands — is truthy and callable, and only rejects with

"Unknown command" when invoked. Fix probe NAMES (e.g. `'listByPage'` → `'list'`), treat an

"Unknown command" rejection as a typo, and never feature-probe by calling entity commands

(a probing insert creates real records).

- `zzTakeoff.darkMode`, `zzTakeoff.post/on`, and `window.zzTakeoff` presence checks still work


## 8. Insert behavior (usually no code change)


Inserts honor an explicit `order` and default to the next slot under the parent (folders sort

first). Typed non-folder takeoffs get an auto color unless `properties.color` is set.

`DrawObjects.insert` defaults `layerId` to the current layer and it must match the linked

takeoff's layer. `startTakeoff` with `properties` auto-assigns nothing — include

`properties.color` or the drawing is invisible. Layers/Libraries/Lists `insert` accepts `{name}`

or `{properties: {name: {value}}}`.


## 9. Discipline


- Fix comments/docs that describe removed APIs, but leave user-visible strings unless misleading.

- Do not touch identifiers that merely LOOK like the old API (a plugin's own `this.commands`

array, an XLSX parser's `l.name`, `canvas.getContext('2d')`).

- Preserve line endings (some files are CRLF) and minified formatting where present.

- After editing, re-scan for: `.commands`, `Storage`/`PluginLocalStorage`, `listByPage`,

`getChildren`/`getDescendants`/`getCutouts`, `clearSelectedDrawObjects`, positional

`DrawObjects.list(x)`/`Lists.list(x)`, single-object `X.update({...Id...})`, capitalized

`.button` comparisons, and flat `.name`/`.id` reads on layer/library/list records — including

inside strings, probes, fallback chains, and comments.

0
You must be logged in to post replies. If you don't have an account you can signup here.