softr-vibe-coding 1.11.0 → 1.11.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,14 @@ All notable changes to this skill are documented here. Versions follow [Semantic
4
4
 
5
5
  Entries from 1.3.1 onward are generated automatically from git commit subjects between version bumps (see `.github/workflows/publish.yml`). Entries before 1.3.1 were backfilled by hand from the existing commit history.
6
6
 
7
+ ## [1.11.2] - 2026-07-08
8
+ - Bump to 1.11.2 — publish recordId-less useRecord note and input.textAsync correction
9
+ - Correct input.textAsync in airtable-automations.md — it is not a real method in EITHER Airtable scripting environment; calling it in the Scripting Extension throws TypeError: input.textAsync is not a function. buttonsAsync is the only interactive runtime prompt; free-text values (e.g. an API key) go through an input.config({...}) setting at the top or a hardcoded constant. Also generalize the Automation Scripts bullet to "no interactive prompts".
10
+ - Document recordId-less useRecord via Studio data binding
11
+
12
+ ## [1.11.1] - 2026-06-12
13
+ - Document the tabbed-list visibility gotcha in native-block-filters.md — Softr renders tabs with Radix and keeps the INACTIVE tab's blocks in the DOM (display:none, not unmounted), so a data-block-id lookup on a tabbed page matches BOTH tabs' lists; injecting a custom control into the hidden one makes it 'disappear' on tab switch. Add §4d: require the matched block to be visible (offsetParent !== null) and home into whichever tab is active so one control follows all tabs (each tab-list keeps its own conditional filter; ~250ms interval for snappier flips), plus a Gotchas bullet. Bump to 1.11.1
14
+
7
15
  ## [1.11.0] - 2026-06-12
8
16
  - Add references/native-block-filters.md — dynamic date / URL-param filters and custom filter controls on native Softr List/Grid blocks. Covers driving a native block's conditional filter from a Custom Code Static block via {URL_PARAM:…}, the empty-param 'match nothing' wide-range sentinel that otherwise empties the list on load, injecting the control into the block's filter row scoped by data-block-id (a page-wide text search wrongly matches the nav 'Clients' link), resolving the row by filter-label-text + lowest-common-ancestor instead of hashed chip classes, and the key gotcha that Softr re-renders its List/Grid block and discards injected nodes so the control must be re-homed on a short interval rather than relocated once. Includes a full worked date-range picker (wide-range sentinel + wait-for-both + Clear + relocation + re-render survival) and a DevTools how-to for finding data-block-id and the filter row. Wire into the SKILL.md reference table and README structure listing; bump to 1.11.0
9
17
 
@@ -84,6 +84,8 @@ var result = useRecord({
84
84
  });
85
85
  ```
86
86
 
87
+ **`recordId` can be omitted when Softr Studio supplies the record context.** `useRecord({ select })` with no `recordId` loads the record the block is bound to via its data-source binding in Studio — verified by deployed block, July 2026 (an Airtable-backed stats block rendered live values this way). Keep `useCurrentRecordId()` + explicit `recordId` as the pattern for URL-driven detail pages (`/page?recordId=...`). When editing an existing **working** block that already omits `recordId`, leave the call shape as-is: adding an explicit `recordId` from `useCurrentRecordId()` can change behavior on pages whose URL carries no `recordId` param. Corollary for reviews: a recordId-less `useRecord` is NOT by itself a defect — check whether the block is deployed and loading data before flagging it.
88
+
87
89
  ## useLinkedRecords -- Fetch Linked/Related Options
88
90
 
89
91
  ```jsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "softr-vibe-coding",
3
- "version": "1.11.0",
3
+ "version": "1.11.2",
4
4
  "description": "Claude Code skill for generating production-ready Softr Vibe Coding blocks (JSX). Installs into ~/.claude/skills/ and auto-updates on each Claude Code session.",
5
5
  "bin": {
6
6
  "softr-vibe-coding": "./bin/cli.js"
@@ -28,7 +28,7 @@ Background-only. No UI. Receives input from the automation trigger; can output v
28
28
  - Output to downstream steps via `output.set(key, value)`.
29
29
  - `console.log()` appears in the automation's run log (NOT the browser console). This is your ONLY progress / debug surface.
30
30
  - **NO `output.markdown`, NO `output.text`, NO `output.table`, NO `output.inspect`** — those throw `TypeError: output.<x> is not a function` here. They're Scripting-Extension-only.
31
- - **NO `input.buttonsAsync` / `input.textAsync`** — interactive UI is not available in the background runner.
31
+ - **NO interactive prompts** (`input.buttonsAsync` and friends) — interactive UI is not available in the background runner. (Note: `input.textAsync` is not a real method in *either* environment — see the Scripting Extension section.)
32
32
  - Execution cap: 120 seconds (recently raised from 30s).
33
33
  - Can be triggered manually via the "Test" button on the automation, useful for one-shot scripts you don't want to set up a real trigger for.
34
34
 
@@ -59,7 +59,7 @@ await main();
59
59
  Foreground, interactive. Used for one-off transforms, bulk fixes, building admin dashboards. Has a richer API. Requires installing the "Scripting" extension in the base — most bases don't have it by default.
60
60
 
61
61
  - Configure interactive inputs via `input.config({ title, items: [...] })` — note: takes an OBJECT here, unlike Automation Scripts.
62
- - Show prompts with `await input.buttonsAsync(prompt, options)`, `input.textAsync(...)`, etc.
62
+ - Show prompts with `await input.buttonsAsync(prompt, options)` — this is the **only** interactive runtime prompt. **There is no `input.textAsync`**; calling it throws `TypeError: input.textAsync is not a function`. For a free-text value (e.g. an API key), declare it as an `input.config({...})` setting at the top, or hardcode a constant for a one-off script.
63
63
  - Render output with `output.markdown(...)`, `output.table(...)`.
64
64
  - `console.log()` shows in the browser devtools console.
65
65
 
@@ -172,6 +172,29 @@ setInterval(ensureHomed, 500); // and re-home after every Softr re-render
172
172
 
173
173
  `ensureHomed` is idempotent (it only re-appends when the control is *not* already in the row), so the interval is cheap and can't loop.
174
174
 
175
+ ### 4d. Tabbed pages — home into the VISIBLE tab's list ⚠️
176
+ **Softr renders tabs with Radix, and the *inactive* tab's blocks stay in the DOM — just `display:none`, not unmounted.** So on a tabbed page (e.g. a worker "Pending / All" view) *both* tabs' list blocks match a `data-block-id` lookup. Grab the hidden one and your control gets injected into the invisible panel — it looks like it "vanished" the moment the user switches tabs.
177
+
178
+ Two rules:
179
+ 1. **Require the matched block to be visible** — `el.offsetParent !== null` (it's `null` when the element or an ancestor tab panel is `display:none`). Skip hidden matches.
180
+ 2. **One control can serve every tab** — list all the tab-lists' block-ids and home into whichever is visible; the re-home interval then makes the control *follow the active tab*.
181
+
182
+ ```js
183
+ // Both tab lists. Softr keeps the INACTIVE tab's list in the DOM (hidden), so an id-only
184
+ // match could grab the wrong one — only the VISIBLE tab's block should win.
185
+ var LIST_BLOCK_IDS = ["<pending-tab-list-id>", "<all-tab-list-id>"];
186
+
187
+ function activeListBlock() {
188
+ for (var i = 0; i < LIST_BLOCK_IDS.length; i++) {
189
+ var el = document.querySelector('[data-block-id="' + LIST_BLOCK_IDS[i] + '"]');
190
+ if (el && el.offsetParent !== null) return el; // offsetParent === null → in a hidden tab
191
+ }
192
+ return null;
193
+ }
194
+ ```
195
+
196
+ Use `activeListBlock()` in place of the single-id lookup inside `findFilterRow()`. Each tab-list still needs its OWN copy of the conditional filter (the control only sets the URL params; every list reads them independently). A ~250ms interval feels snappier than 500ms when the user flips tabs.
197
+
175
198
  ---
176
199
 
177
200
  ## 5. Full worked example — date-range picker that joins the filter row
@@ -280,6 +303,7 @@ A complete Custom Code Static block: range picker with the wide-range sentinel,
280
303
 
281
304
  - **Empty `{URL_PARAM}` = match NOTHING.** A blank/missing param empties a filtered list. Seed a wide sentinel range (and map it back to blank inputs) so "no selection" shows all. §2.
282
305
  - **Softr re-renders its list block and discards injected nodes.** One-shot relocation vanishes on the first interaction. Re-home on an interval/observer. §4c.
306
+ - **Tabbed pages keep the inactive tab's list in the DOM (`display:none`).** A `data-block-id` match can grab the hidden tab's copy → the injected control "disappears" on tab switch. Require `offsetParent !== null` and home into the visible list; one control can follow all tabs. §4d.
283
307
  - **Page-wide DOM searches hit the native chrome.** Matching by text "Client" also matches a "Clients" nav link → control lands in the sidebar. Always scope to the block's `data-block-id`. §4a.
284
308
  - **Filter chips have only hashed classes** (`a0e85ef_…`) that change on deploys. Anchor on your **filter label text + lowest common ancestor**; keep the hash as a fallback only. §4b.
285
309
  - **`window.location.reload()` is how Softr re-reads the param.** `replaceState` alone updates the URL but won't re-run the conditional filter — you must reload.
@@ -65,6 +65,8 @@ var recordId = useCurrentRecordId();
65
65
  var result = useRecord({ recordId: recordId, select: select });
66
66
  ```
67
67
 
68
+ `recordId` may be omitted when the block's Studio data binding supplies the record context (verified by deployed block, July 2026) — see [reading.md](../datasources/reading.md#userecord----fetch-a-single-record).
69
+
68
70
  ## Current User
69
71
 
70
72
  ```jsx