@colixsystems/widget-sdk 0.24.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,6 +18,7 @@ The data layer lives in **four separate domain-client packages**, each instantia
18
18
  | Group | Hook (signature) | Returns | Reads / scope |
19
19
  | ----- | ---------------- | ------- | ------------- |
20
20
  | **CORE** | `useTheme()` | `{ colors, spacing, radii, typography }` | `ctx.workspace.theme` — no scope |
21
+ | **CORE** | `useWidgetStyle()` | `{ [styleField]: value }` | `ctx.props.style` — no scope. The author-set per-widget style values declared in `manifest.styleSchema`; apply each onto whatever element you choose. |
21
22
  | **CORE** | `useUser()` | `{ id, email, display_name, roles, group_ids }` | `ctx.user` (snake_case verbatim; `id` null when anonymous) — no scope |
22
23
  | **CORE** | `useNavigation()` | `{ goTo, goBack, push, replace, back, currentRoute }` | `ctx.navigation` — no scope (external URLs use the `Linking` primitive) |
23
24
  | **CORE** | `useWidgetEvent(name)` | `(payload?) => void` | `ctx.events.emit` — no scope |
@@ -45,7 +46,31 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
45
46
 
46
47
  ## Status
47
48
 
48
- `v0.23.0` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
49
+ `v0.26.0` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
50
+
51
+ ### What's new in 0.26.0
52
+
53
+ **New `pageRef` propertySchema type (REQ-WDG-PAGEREF).** A page picker: the Studio Properties Panel renders a dropdown of the app's pages and stores the chosen page's bare UUID. A widget reads the prop and navigates with `useNavigation().goTo(pageId, params)` — the cross-platform replacement for the old per-widget "navigate on press" event wiring. tenant-copy remaps the stored page id to the copied page. Additive: no existing export or type changed signature.
54
+
55
+ ### What's new in 0.25.0
56
+
57
+ **Per-widget styling via `styleSchema` (REQ-THEME-13).**
58
+
59
+ - **New optional `manifest.styleSchema` field.** Same shape and types as `propertySchema` (`color`, `number`, `select`, `boolean`, …) — it declares the styling options the widget exposes. Example:
60
+ ```js
61
+ styleSchema: {
62
+ cardBackground: { type: "color", label: "Card background" },
63
+ cardRadius: { type: "number", label: "Corner radius", validation: { min: 0, max: 48 } },
64
+ valueColor: { type: "color", label: "Value color" },
65
+ }
66
+ ```
67
+ - **The Studio renders a "Style" section** from `styleSchema` using the same `SchemaForm` engine as `propertySchema`. The author's resolved values are persisted under the node's `props.style`.
68
+ - **New `useWidgetStyle()` hook** returns `props.style` (an object keyed by your style-field names). Read `style.<field>` and apply each onto whatever element you choose — the host never auto-applies style, so the widget controls placement:
69
+ ```jsx
70
+ const style = useWidgetStyle();
71
+ <View style={[styles.card, style.cardBackground && { backgroundColor: style.cardBackground }]}>
72
+ ```
73
+ - **`CONTRACT.version` → `1.15.0`** (additive: one optional manifest field + one hook). No existing field, hook, primitive, or token changed shape.
49
74
 
50
75
  ### What's new in 0.23.0
51
76
 
package/dist/contract.cjs CHANGED
@@ -47,6 +47,23 @@ const HOOKS = [
47
47
  requiredContextSlice: ["workspace.theme"],
48
48
  scopes: null,
49
49
  },
50
+ {
51
+ name: "useWidgetStyle",
52
+ signature: "useWidgetStyle()",
53
+ description:
54
+ "Returns the author-set per-widget style values (REQ-THEME-13) — the " +
55
+ "resolved object the host delivers under props.style, keyed by the " +
56
+ "style-field names the widget declares in manifest.styleSchema. Read " +
57
+ "props.style.<field> and apply each onto whatever element you choose " +
58
+ "(spread AFTER your intrinsic styles so author choices win). Returns an " +
59
+ "empty object when nothing is set, so reads are always safe. Equivalent " +
60
+ "to reading the `style` prop directly.",
61
+ returnShape: {
62
+ "<styleFieldName>": "the author-set value (string/number/… per the styleSchema def)",
63
+ },
64
+ requiredContextSlice: ["props"],
65
+ scopes: null,
66
+ },
50
67
  {
51
68
  name: "useI18n",
52
69
  signature: "useI18n()",
@@ -601,6 +618,20 @@ const MANIFEST_SCHEMA = {
601
618
  description: "Map of prop name to property definition.",
602
619
  default: {},
603
620
  },
621
+ styleSchema: {
622
+ type: "object",
623
+ required: false,
624
+ description:
625
+ "Optional (REQ-THEME-13). Map of style-field name to definition — the " +
626
+ "SAME shape and types as propertySchema (color, number, select, " +
627
+ "boolean, …). Declares the per-instance styling options the widget " +
628
+ "exposes; the Studio Properties Panel renders a \"Style\" section from " +
629
+ "it. The author's resolved values are delivered to the widget under " +
630
+ "props.style (one object keyed by style-field name); the widget reads " +
631
+ "props.style.<field> and applies each wherever it chooses. The host " +
632
+ "never auto-applies style — the widget owns placement.",
633
+ default: {},
634
+ },
604
635
  events: {
605
636
  type: "object[]",
606
637
  required: true,
@@ -1169,7 +1200,16 @@ const CONTRACT = deepFreeze({
1169
1200
  // when the host's client predates realtime or no WebSocket is available,
1170
1201
  // so widgets degrade to polling on both platforms. No existing hook,
1171
1202
  // primitive, or contract field changed — minor bump on the pre-1.0 channel.
1172
- version: "1.14.0",
1203
+ //
1204
+ // 1.15.0: additive (REQ-THEME-13) — per-widget styling via `styleSchema`. New
1205
+ // optional manifest field `styleSchema`: the SAME shape as propertySchema
1206
+ // (color/number/select/… fields), declaring the per-instance styling
1207
+ // options a widget exposes. The Studio renders a "Style" section from it;
1208
+ // the resolved values are delivered to the widget under props.style and
1209
+ // the widget applies them itself (the host never auto-styles). New
1210
+ // `useWidgetStyle()` hook returns props.style. No existing field, hook,
1211
+ // primitive, or token changed shape — minor bump on the pre-1.0 channel.
1212
+ version: "1.15.0",
1173
1213
  hooks: HOOKS,
1174
1214
  primitives: PRIMITIVES,
1175
1215
  manifestSchema: MANIFEST_SCHEMA,
package/dist/contract.js CHANGED
@@ -47,6 +47,23 @@ const HOOKS = [
47
47
  requiredContextSlice: ["workspace.theme"],
48
48
  scopes: null,
49
49
  },
50
+ {
51
+ name: "useWidgetStyle",
52
+ signature: "useWidgetStyle()",
53
+ description:
54
+ "Returns the author-set per-widget style values (REQ-THEME-13) — the " +
55
+ "resolved object the host delivers under props.style, keyed by the " +
56
+ "style-field names the widget declares in manifest.styleSchema. Read " +
57
+ "props.style.<field> and apply each onto whatever element you choose " +
58
+ "(spread AFTER your intrinsic styles so author choices win). Returns an " +
59
+ "empty object when nothing is set, so reads are always safe. Equivalent " +
60
+ "to reading the `style` prop directly.",
61
+ returnShape: {
62
+ "<styleFieldName>": "the author-set value (string/number/… per the styleSchema def)",
63
+ },
64
+ requiredContextSlice: ["props"],
65
+ scopes: null,
66
+ },
50
67
  {
51
68
  name: "useI18n",
52
69
  signature: "useI18n()",
@@ -177,8 +194,7 @@ const HOOKS = [
177
194
  signature: "useDatastoreMutation(tableId)",
178
195
  returnShape: {
179
196
  create: "(record) => Promise<Record> // rejects with DatastoreError",
180
- update:
181
- "(id, partial) => Promise<Record> // rejects with DatastoreError",
197
+ update: "(id, partial) => Promise<Record> // rejects with DatastoreError",
182
198
  delete: "(id) => Promise<void> // rejects with DatastoreError",
183
199
  },
184
200
  requiredContextSlice: ["datastore.records"],
@@ -281,13 +297,7 @@ const HOOKS = [
281
297
  scopes: ["groups.read:*"],
282
298
  },
283
299
  // REQ-ACL-06 / REQ-ACL-RELINHERIT-05 — per-record VirtualPermission
284
- // management for a single record. Reads the injected datastore-client at
285
- // ctx.datastore.records(tableId).permissions(recordId) (the recordPermissions
286
- // facade was folded into the datastore-client). The backend gates the call
287
- // on `can_grant` for the target record (Studio owners short-circuit;
288
- // APP_USER actors must hold `can_grant` via REQ-ACL-05 / REQ-ACL-06). A
289
- // widget that declares the scope but whose caller lacks the grant receives
290
- // `PermissionError { code: 'FORBIDDEN' }`.
300
+ // management for a single record. Mirror of contract.js.
291
301
  {
292
302
  name: "useRecordPermissions",
293
303
  signature: "useRecordPermissions(tableId, recordId)",
@@ -346,7 +356,7 @@ const HOOKS = [
346
356
  requiredContextSlice: ["datastore.records"],
347
357
  scopes: ["datastore.read:<table>"],
348
358
  },
349
- // REQ-WSDK-PLATFORM §6 — Tier A SDK hooks.
359
+ // REQ-WSDK-PLATFORM §6 — Tier A SDK hooks. Mirror of contract.js.
350
360
  {
351
361
  name: "useClipboard",
352
362
  signature: "useClipboard()",
@@ -384,7 +394,13 @@ const HOOKS = [
384
394
  },
385
395
  ];
386
396
 
387
- // REQ-WSDK-RN-WEB: see contract.cjs for the source-of-truth comment.
397
+ // REQ-WSDK-RN-WEB: the SDK exposes the React Native primitive API
398
+ // (backed by react-native-web on the browser, by react-native on the
399
+ // exported Expo app). The contract describes the *cross-platform
400
+ // subset* the SDK re-exports; the per-prop reference for each
401
+ // component is whatever React Native documents. Adding a new primitive
402
+ // = add the name to `./primitives.js` + `./primitives.native.js` (both
403
+ // one-line re-exports) and append it to this list.
388
404
  const PRIMITIVES = [
389
405
  {
390
406
  name: "Text",
@@ -602,6 +618,20 @@ const MANIFEST_SCHEMA = {
602
618
  description: "Map of prop name to property definition.",
603
619
  default: {},
604
620
  },
621
+ styleSchema: {
622
+ type: "object",
623
+ required: false,
624
+ description:
625
+ "Optional (REQ-THEME-13). Map of style-field name to definition — the " +
626
+ "SAME shape and types as propertySchema (color, number, select, " +
627
+ "boolean, …). Declares the per-instance styling options the widget " +
628
+ "exposes; the Studio Properties Panel renders a \"Style\" section from " +
629
+ "it. The author's resolved values are delivered to the widget under " +
630
+ "props.style (one object keyed by style-field name); the widget reads " +
631
+ "props.style.<field> and applies each wherever it chooses. The host " +
632
+ "never auto-applies style — the widget owns placement.",
633
+ default: {},
634
+ },
605
635
  events: {
606
636
  type: "object[]",
607
637
  required: true,
@@ -636,7 +666,8 @@ const MANIFEST_SCHEMA = {
636
666
 
637
667
  const WIDGET_CONTEXT_SHAPE = {
638
668
  props: {
639
- description: "Resolved property values, shape per manifest.propertySchema.",
669
+ description:
670
+ "Resolved property values, shape per manifest.propertySchema.",
640
671
  required: true,
641
672
  fields: {},
642
673
  },
@@ -750,10 +781,7 @@ const WIDGET_CONTEXT_SHAPE = {
750
781
  error: "function",
751
782
  },
752
783
  },
753
- // REQ-WSDK-PLATFORM §6 — backs useToast(). The host installs its own
754
- // workspace-themed toast renderer here; if omitted, the SDK's useToast
755
- // hook falls back to a CustomEvent on web / console.log on native so
756
- // widget code still runs without a host integration.
784
+ // REQ-WSDK-PLATFORM §6 — backs useToast(). Mirror of contract.js.
757
785
  toast: {
758
786
  description:
759
787
  "Optional host toast slot. { showToast({ kind, message }): void }. " +
@@ -787,8 +815,14 @@ const BUNDLE_EXPORT_CONTRACT = [
787
815
  ];
788
816
 
789
817
  // REQ-WSDK-PLATFORM (docs/design/req-widget-sdk-cross-platform-primitives.md
790
- // §3.5, §8): `fetch` and `XMLHttpRequest` are NOT banned. See contract.cjs
791
- // for the source-of-truth comment.
818
+ // §3.5, §8): `fetch` and `XMLHttpRequest` are NOT banned. Widgets may call
819
+ // third-party APIs directly. Same-origin requests to the host's own
820
+ // `/api/*` surface are rejected at runtime by the WidgetContextProvider's
821
+ // network gate (`no host-api access from widgets`) — the JWT token is
822
+ // never shared with widget code, so the call would 401 anyway; the runtime
823
+ // gate makes the failure mode "blocked" instead of "401 noise". A soft
824
+ // linter warning (`no-host-api-url`) flags obvious host-URL substrings at
825
+ // submission so authors learn the rule statically.
792
826
  const BANNED_APIS = [
793
827
  { identifier: "eval", reason: "Arbitrary code evaluation." },
794
828
  {
@@ -823,8 +857,22 @@ const BANNED_APIS = [
823
857
  { identifier: "globalThis", reason: "Host environment escape." },
824
858
  ];
825
859
 
826
- // REQ-WSDK-PLATFORM §3.4, §5: vetted package allowlist. See contract.cjs
827
- // for the source-of-truth comment.
860
+ // REQ-WSDK-PLATFORM §3.4, §5: vetted package allowlist. Each entry is a
861
+ // specifier the widget bundle may import as a bare module specifier. The
862
+ // linter validates every `import ... from "<spec>"` line against this
863
+ // list; specifiers not on the list fail the lint. The compiler reads it
864
+ // to know which packages to add to the exported Expo app's package.json.
865
+ //
866
+ // `platforms` is one or both of "web" / "native". A widget that imports a
867
+ // native-only package implicitly drops "web" from the package's effective
868
+ // `supportedPlatforms` (and vice versa) — the marketplace upload pipeline
869
+ // surfaces the derived set so the listing shows honest badges.
870
+ //
871
+ // Adding a package is a CONTRACT change. Review burden: confirm the
872
+ // package does what it claims, has a credible maintainer, and the
873
+ // import shape (named exports, default export) is stable. After that,
874
+ // every widget using the package inherits the review — the marketplace
875
+ // reviewer only spot-checks usage, not the package source.
828
876
  const VETTED_IMPORTS = [
829
877
  {
830
878
  specifier: "react",
@@ -1006,8 +1054,19 @@ const VETTED_IMPORTS = [
1006
1054
  },
1007
1055
  ];
1008
1056
 
1057
+ // Back-compat shape — every existing consumer (widgetLoader, the static
1058
+ // analyzer's DEPENDENCY_ALLOWLIST, the AI prompt's "Allowed imports" line)
1059
+ // reads a plain `string[]` from `CONTRACT.allowedBareImports`. Derive it
1060
+ // from the rich list so a single edit in VETTED_IMPORTS propagates to
1061
+ // every surface.
1009
1062
  const ALLOWED_BARE_IMPORTS = VETTED_IMPORTS.map((v) => v.specifier);
1010
1063
 
1064
+ // REQ-WSDK-PLATFORM §3.5: host-API URL patterns the linter scans for as a
1065
+ // soft warning. None of these block the lint by themselves — they prompt
1066
+ // the marketplace reviewer to spot-check, and they help authors learn the
1067
+ // rule statically. Strings appear as literal substring matches (the URL
1068
+ // is reachable in widget code by composing it at runtime, so this is
1069
+ // best-effort).
1011
1070
  const HOST_API_URL_PATTERNS = [
1012
1071
  "/api/v1",
1013
1072
  "/uploads/",
@@ -1036,24 +1095,43 @@ function widgetTranslationKey(id, key) {
1036
1095
  }
1037
1096
 
1038
1097
  const CONTRACT = deepFreeze({
1039
- // 1.7.0: additive — new useDatastoreSchema(tableId) hook + the
1040
- // datastore.schema host-context slice it reads (resolves a table's column
1041
- // structure at runtime via the existing ACL-gated GET /tables/:id).
1098
+ // REQ-WSDK-PLATFORM bump:
1099
+ // - `vettedImports` is a new field (rich allowlist with platforms +
1100
+ // category + description).
1101
+ // - `hostApiUrlPatterns` is a new field (soft-warning substrings).
1102
+ // - `bannedApis` no longer lists `fetch` / `XMLHttpRequest`.
1103
+ // - `allowedBareImports` is now derived from `vettedImports` (same
1104
+ // shape; same contents grow with each vetted addition).
1105
+ // Permissive-direction change: minor bump on the contract's own
1106
+ // versioning (per CLAUDE.md §4, pre-1.0 minor is the breaking channel —
1107
+ // the package.json version bumps accordingly).
1108
+ //
1109
+ // 1.6.0: additive — `themeTokens.colors` gains `secondary` + `onSecondary`
1110
+ // so the tenant's Theme Settings "Secondary Color" flows through
1111
+ // `useTheme().colors.secondary` (Button secondary variant + any widget
1112
+ // that wants the brand's second accent).
1113
+ //
1114
+ // 1.7.0: additive — new `useDatastoreSchema(tableId)` hook + the
1115
+ // `datastore.schema` host-context slice it reads. Lets a widget resolve a
1116
+ // stored columnId to its column name / dataType / relation target at
1117
+ // runtime (Form Builder renders inputs by column type). Reads the existing
1118
+ // ACL-gated `GET /tables/:id` — structure only, no row data.
1042
1119
  //
1043
1120
  // 1.8.0: two additive features.
1044
- // - REQ-WIDGET-ACTION — manifests may declare an optional `actions` array:
1045
- // server-side cron/record-triggered scripts the operator enables per
1046
- // tenant, run in the existing isolated-vm action runner (never in the
1047
- // rendered app). New fields actionTriggerTypes / actionScriptGlobals /
1048
- // actionScriptMaxBytes feed the docs + agent prompt. New ADMINISTRATION
1049
- // manifest category (REQ-USERMGMT-06).
1050
- // - REQ-ACL-06 — new useRecordPermissions(tableId, recordId) hook + the
1051
- // recordPermissions host-context slice it reads + the acl.write:records
1121
+ // - REQ-WIDGET-ACTION — manifests may declare an optional `actions` array.
1122
+ // Each entry is a server-side action (cron- or record-triggered JS) the
1123
+ // operator enables per tenant; it runs in the existing isolated-vm action
1124
+ // runner, never in the rendered app. New contract fields
1125
+ // `actionTriggerTypes`, `actionScriptGlobals`, and `actionScriptMaxBytes`
1126
+ // let the docs + agent prompt + validator derive the grammar from one
1127
+ // source. New `ADMINISTRATION` manifest category (REQ-USERMGMT-06).
1128
+ // - REQ-ACL-06 new `useRecordPermissions(tableId, recordId)` hook + the
1129
+ // `recordPermissions` host-context slice it reads + the `acl.write:records`
1052
1130
  // scope it gates on. Hits the existing REQ-ACL-06
1053
- // /api/v1/tables/:tableId/records/:recordId/permissions REST surface;
1054
- // the host normalises the wire shape into a single principalType+
1055
- // principalId pair widgets branch on. Caller still needs canGrant on
1056
- // the target record (REQ-ACL-RELINHERIT-05).
1131
+ // /api/v1/tables/:tableId/records/:recordId/permissions REST surface; the
1132
+ // host normalises the wire shape into a single principalType+principalId
1133
+ // pair widgets branch on. Caller still needs canGrant on the target
1134
+ // record (REQ-ACL-RELINHERIT-05).
1057
1135
  //
1058
1136
  // 1.9.0: host-contract change (REQ-WSDK-DOMAIN-CLIENTS) — the host now
1059
1137
  // injects domain-client INSTANCES on the WidgetContext rather than
@@ -1122,7 +1200,16 @@ const CONTRACT = deepFreeze({
1122
1200
  // when the host's client predates realtime or no WebSocket is available,
1123
1201
  // so widgets degrade to polling on both platforms. No existing hook,
1124
1202
  // primitive, or contract field changed — minor bump on the pre-1.0 channel.
1125
- version: "1.14.0",
1203
+ //
1204
+ // 1.15.0: additive (REQ-THEME-13) — per-widget styling via `styleSchema`. New
1205
+ // optional manifest field `styleSchema`: the SAME shape as propertySchema
1206
+ // (color/number/select/… fields), declaring the per-instance styling
1207
+ // options a widget exposes. The Studio renders a "Style" section from it;
1208
+ // the resolved values are delivered to the widget under props.style and
1209
+ // the widget applies them itself (the host never auto-styles). New
1210
+ // `useWidgetStyle()` hook returns props.style. No existing field, hook,
1211
+ // primitive, or token changed shape — minor bump on the pre-1.0 channel.
1212
+ version: "1.15.0",
1126
1213
  hooks: HOOKS,
1127
1214
  primitives: PRIMITIVES,
1128
1215
  manifestSchema: MANIFEST_SCHEMA,
package/dist/hooks.js CHANGED
@@ -83,6 +83,26 @@ export function useTheme() {
83
83
  return ctx.workspace.theme;
84
84
  }
85
85
 
86
+ /**
87
+ * REQ-THEME-13 — returns the author-set per-widget style values: the object the
88
+ * host delivers under `props.style`, keyed by the style-field names the widget
89
+ * declares in its `manifest.styleSchema`. Read `style.<field>` and apply each
90
+ * onto whatever element you choose (spread AFTER your intrinsic styles so the
91
+ * author's choice wins):
92
+ *
93
+ * const style = useWidgetStyle();
94
+ * <View style={[styles.card, style.cardBackground && { backgroundColor: style.cardBackground }]}>
95
+ *
96
+ * Returns an empty object when nothing is set, so reads are always safe. This
97
+ * is equivalent to reading the `style` prop directly — the hook is a
98
+ * convenience so a widget needn't thread `style` through its prop signature.
99
+ */
100
+ export function useWidgetStyle() {
101
+ const ctx = useWidgetContextOrThrow("useWidgetStyle");
102
+ const style = ctx.props ? ctx.props.style : undefined;
103
+ return style && typeof style === "object" ? style : {};
104
+ }
105
+
86
106
  /**
87
107
  * Returns the active end-user identity VERBATIM from the host, e.g.
88
108
  * `{ id, email, display_name, roles, group_ids }` (snake_case fields).
package/dist/index.d.ts CHANGED
@@ -36,6 +36,9 @@ export type WidgetPropertyType =
36
36
  // REQ-USERMGMT M4 / §4.8: Group picker that emits a bare
37
37
  // AppUserGroup UUID into the page JSON.
38
38
  | "groupRef"
39
+ // REQ-WDG-PAGEREF: page picker that emits a bare AppPage UUID; a widget
40
+ // reads it and navigates via useNavigation().goTo(pageId, params).
41
+ | "pageRef"
39
42
  | "expression"
40
43
  | "eventBinding"
41
44
  | "object"
@@ -184,6 +187,14 @@ export interface WidgetManifest {
184
187
  minAppStudioVersion: string;
185
188
  requestedScopes: WidgetScope[];
186
189
  propertySchema: WidgetPropertySchema;
190
+ /**
191
+ * REQ-THEME-13 — optional per-widget styling schema. Same shape and types as
192
+ * `propertySchema`; declares the styling options the widget exposes. The
193
+ * Studio renders a "Style" section from it and delivers the author's resolved
194
+ * values to the widget under `props.style` (read them via `useWidgetStyle()`
195
+ * or the `style` prop). The widget applies each value itself.
196
+ */
197
+ styleSchema?: WidgetPropertySchema;
187
198
  events: WidgetEventDescriptor[];
188
199
  /**
189
200
  * Optional datastore template seeded into the tenant's workspace when
@@ -556,6 +567,14 @@ export function usePayments(): PaymentsApi;
556
567
 
557
568
  export function useTheme(): ThemeTokens;
558
569
 
570
+ /**
571
+ * REQ-THEME-13 — the author-set per-widget style values (the `props.style`
572
+ * object), keyed by the names declared in `manifest.styleSchema`. Returns an
573
+ * empty object when nothing is set. Apply each value onto whatever element you
574
+ * choose; the host never auto-applies style.
575
+ */
576
+ export function useWidgetStyle(): Record<string, unknown>;
577
+
559
578
  /**
560
579
  * Stateful single-record fetch hook. Returns `{ data, loading, error,
561
580
  * refetch }`. `data` is one row or `null` (never an array).
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ export {
24
24
  useWidgetEvent,
25
25
  usePayments,
26
26
  useTheme,
27
+ useWidgetStyle,
27
28
  useI18n,
28
29
  useUser,
29
30
  useFill,
@@ -24,6 +24,7 @@ export {
24
24
  useWidgetEvent,
25
25
  usePayments,
26
26
  useTheme,
27
+ useWidgetStyle,
27
28
  useI18n,
28
29
  useUser,
29
30
  useFill,
@@ -20,6 +20,12 @@ const VALID_TYPES = new Set([
20
20
  // value is the raw uuid string so the tenant-copy walker can remap it
21
21
  // through `_remapJson` without per-prop hooks.
22
22
  "groupRef",
23
+ // REQ-WDG-PAGEREF: `pageRef` is a page picker that emits a bare AppPage
24
+ // UUID into the page JSON. Renders a page `<select>` in the Studio
25
+ // Properties Panel; a widget reads it and navigates via
26
+ // `useNavigation().goTo(pageId, params)`. Bare uuid string — tenant-copy
27
+ // remaps it through the layout walk's pageId map (see tenant-copy.service.js).
28
+ "pageRef",
23
29
  "expression", "eventBinding",
24
30
  "object", "array",
25
31
  ]);
@@ -102,6 +108,7 @@ function coerceLeaf(def, value, path, errors) {
102
108
  case "columnRef":
103
109
  case "recordBinding":
104
110
  case "groupRef":
111
+ case "pageRef":
105
112
  case "expression":
106
113
  case "eventBinding":
107
114
  if (typeof value !== "string") errors.push(`${path}: expected string`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colixsystems/widget-sdk",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",