@colixsystems/widget-sdk 0.37.0 → 0.38.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 +12 -2
- package/dist/contract.cjs +80 -1
- package/dist/contract.js +80 -1
- package/dist/hooks.js +33 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ The data layer lives in **four separate domain-client packages**, each instantia
|
|
|
26
26
|
| **CORE** | `useFill()` | `boolean` | `ctx.fill` — no scope. `true` when the host sized this widget to fill its page-grid tile's reserved height (containers + media fill by default; the author can override per tile). Media-style widgets switch to a `flex: 1` / `height: "100%"` layout; others ignore it. Defaults `false`. |
|
|
27
27
|
| **CORE** | `useClipboard()` | `{ copy, paste, hasContent }` | platform clipboard (web `navigator.clipboard` / native `expo-clipboard`); rejects with `ClipboardError` — no scope |
|
|
28
28
|
| **CORE** | `useToast()` | `{ showToast }` | `ctx.toast.showToast` (falls back to a CustomEvent / console) — no scope |
|
|
29
|
-
| **CORE** | `useI18n()` | `{ t, locale }` | `ctx.i18n` — no scope. `t(key)` resolves the widget-namespaced key (`widget.<id>.<key>`, declared in `manifest.translations`) then
|
|
29
|
+
| **CORE** | `useI18n()` | `{ t, locale }` | `ctx.i18n` — no scope. `t(key)` resolves the widget-namespaced key (`widget.<id>.<key>`, declared in `manifest.translations`) first, then a **predefined shared key** (`shared.<key>`) when `key` is one of the standard strings (`submit`, `cancel`, `save`, `loading`, …), then the raw key. Use a shared key for an identical default string so it translates once and any per-instance `widget.<id>.<key>` override still wins. |
|
|
30
30
|
| **DATASTORE** (`ctx.datastore`) | `useDatastoreQuery(table, options?)` | `{ data, loading, error, refetch }` | `records(table).list` (unwraps `{ data, meta }` to `data: []`) — `datastore.read:*` |
|
|
31
31
|
| **DATASTORE** | `useDatastoreRecord(table, id)` | `{ data, loading, error, refetch }` | `records(table).get` — `datastore.read:<table>` |
|
|
32
32
|
| **DATASTORE** | `useDatastoreSchema(tableId)` | `{ schema, loading, error, refetch }` | `schema(tableId)` — `datastore.read:<table>` |
|
|
@@ -47,7 +47,17 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
47
47
|
|
|
48
48
|
## Status
|
|
49
49
|
|
|
50
|
-
`v0.
|
|
50
|
+
`v0.38.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**.
|
|
51
|
+
|
|
52
|
+
### What's new in 0.38.0
|
|
53
|
+
|
|
54
|
+
**Predefined SHARED translation keys (REQ-L10N-SHARED).** The standard strings the default widgets repeat ("Submit", "Cancel", "Save", "Loading…", …) now have a tenant-wide shared namespace `shared.<key>` so an identical string is translated **once** and every widget that uses it inherits the translation.
|
|
55
|
+
|
|
56
|
+
- **New contract field `CONTRACT.sharedTranslationKeys`** — the predefined map (`{ <key>: { en } }`) and the single source the host seeder reads.
|
|
57
|
+
- **New exported helpers** `sharedTranslationPrefix()` / `sharedTranslationKey(key)` / `isSharedTranslationKey(key)` (from `@colixsystems/widget-sdk/contract`).
|
|
58
|
+
- **`useI18n().t(key)` resolution is now three-step**: the per-widget key (`widget.<id>.<key>`) first, then the shared key (`shared.<key>`) when `key` is one of the predefined shared keys, then the raw key / fallback. So a default widget that calls `t("submit")` picks up the shared translation with no manual key entry, and an author who sets `widget.<id>.submit` in the Translations admin still overrides **that instance only**. An author-invented bare key is never silently shared.
|
|
59
|
+
- **The host auto-registers the shared keys** for a tenant at workspace-content seed time and whenever a marketplace or AI-generated widget is added — idempotent and non-destructive (an admin edit is never overwritten). Authors manage / translate them in the Studio Translations screen like any other key.
|
|
60
|
+
- **`CONTRACT.version` → `1.28.0`** (additive: one new contract field + three helper exports + the `useI18n` shared-key step). No existing export changed signature.
|
|
51
61
|
|
|
52
62
|
### What's new in 0.37.0
|
|
53
63
|
|
package/dist/contract.cjs
CHANGED
|
@@ -1285,6 +1285,65 @@ function widgetTranslationKey(id, key) {
|
|
|
1285
1285
|
return `widget.${id}.${key}`;
|
|
1286
1286
|
}
|
|
1287
1287
|
|
|
1288
|
+
// REQ-L10N-SHARED — predefined SHARED translation keys for the standard
|
|
1289
|
+
// strings the default widgets repeat ("Submit", "Cancel", "Loading…", …).
|
|
1290
|
+
// They live in ONE tenant-wide namespace (`shared.<key>`) rather than the
|
|
1291
|
+
// per-widget namespace, so identical strings translate ONCE and every widget
|
|
1292
|
+
// that calls `t("<sharedKey>")` resolves the same dictionary value. An author
|
|
1293
|
+
// still overrides any instance by setting `widget.<id>.<key>` in the
|
|
1294
|
+
// Translations admin — `useI18n` resolves the per-widget key FIRST, then the
|
|
1295
|
+
// shared key, then the raw key, so the override is per-instance.
|
|
1296
|
+
//
|
|
1297
|
+
// This is the analogue of `widget.<id>.` for cross-widget reuse: one prefix,
|
|
1298
|
+
// one definition (the hook reads + the backend seeder writes both call these),
|
|
1299
|
+
// so the wire key and the lookup key can never drift.
|
|
1300
|
+
const SHARED_TRANSLATION_PREFIX = "shared.";
|
|
1301
|
+
function sharedTranslationPrefix() {
|
|
1302
|
+
return SHARED_TRANSLATION_PREFIX;
|
|
1303
|
+
}
|
|
1304
|
+
function sharedTranslationKey(key) {
|
|
1305
|
+
return `${SHARED_TRANSLATION_PREFIX}${key}`;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// The predefined set. Map of bare key -> { en } (English default only; the
|
|
1309
|
+
// tenant adds target-language values once in the Translations admin and every
|
|
1310
|
+
// widget inherits them). Adding/removing a key is a CONTRACT change (it grows
|
|
1311
|
+
// the dictionary every tenant gets), so the list lives with the contract and
|
|
1312
|
+
// the seeder reads it from here — never a second copy.
|
|
1313
|
+
const SHARED_TRANSLATION_KEYS = Object.freeze({
|
|
1314
|
+
submit: { en: "Submit" },
|
|
1315
|
+
cancel: { en: "Cancel" },
|
|
1316
|
+
save: { en: "Save" },
|
|
1317
|
+
delete: { en: "Delete" },
|
|
1318
|
+
edit: { en: "Edit" },
|
|
1319
|
+
close: { en: "Close" },
|
|
1320
|
+
confirm: { en: "Confirm" },
|
|
1321
|
+
back: { en: "Back" },
|
|
1322
|
+
next: { en: "Next" },
|
|
1323
|
+
previous: { en: "Previous" },
|
|
1324
|
+
yes: { en: "Yes" },
|
|
1325
|
+
no: { en: "No" },
|
|
1326
|
+
ok: { en: "OK" },
|
|
1327
|
+
loading: { en: "Loading…" },
|
|
1328
|
+
search: { en: "Search" },
|
|
1329
|
+
no_results: { en: "No results" },
|
|
1330
|
+
required: { en: "Required" },
|
|
1331
|
+
retry: { en: "Retry" },
|
|
1332
|
+
add: { en: "Add" },
|
|
1333
|
+
remove: { en: "Remove" },
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1336
|
+
// True when `key` is one of the predefined shared keys — the discriminator
|
|
1337
|
+
// `useI18n` uses to decide whether to try the `shared.<key>` namespace. A key
|
|
1338
|
+
// the author invents is NOT shared (it stays per-widget) so authors can't
|
|
1339
|
+
// accidentally collide with another widget by reusing a bare name.
|
|
1340
|
+
function isSharedTranslationKey(key) {
|
|
1341
|
+
return (
|
|
1342
|
+
typeof key === "string" &&
|
|
1343
|
+
Object.prototype.hasOwnProperty.call(SHARED_TRANSLATION_KEYS, key)
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1288
1347
|
const CONTRACT = deepFreeze({
|
|
1289
1348
|
// REQ-WSDK-PLATFORM bump:
|
|
1290
1349
|
// - `vettedImports` is a new field (rich allowlist with platforms +
|
|
@@ -1486,7 +1545,23 @@ const CONTRACT = deepFreeze({
|
|
|
1486
1545
|
// on `react/jsx-runtime`. Adding them converges the linter onto the same
|
|
1487
1546
|
// contract every other surface already honoured (CLAUDE.md §3). No
|
|
1488
1547
|
// existing entry changed shape — minor bump on the pre-1.0 channel.
|
|
1489
|
-
|
|
1548
|
+
//
|
|
1549
|
+
// 1.28.0: additive (REQ-L10N-SHARED) — predefined SHARED translation keys.
|
|
1550
|
+
// A new tenant-wide namespace `shared.<key>` carries the standard strings
|
|
1551
|
+
// the default widgets repeat ("Submit", "Cancel", "Loading…", …) so an
|
|
1552
|
+
// identical string is translated ONCE and every widget that calls
|
|
1553
|
+
// `t("<sharedKey>")` inherits it. New contract field
|
|
1554
|
+
// `sharedTranslationKeys` (the predefined map, the single source the
|
|
1555
|
+
// backend seeder reads) + new exported helpers `sharedTranslationPrefix()`
|
|
1556
|
+
// / `sharedTranslationKey(key)` / `isSharedTranslationKey(key)`.
|
|
1557
|
+
// `useI18n().t(key)` now resolves the per-widget key first, THEN the
|
|
1558
|
+
// shared key (when `key` is one of the predefined shared keys), then the
|
|
1559
|
+
// raw key — so a default widget picks up the shared translation with no
|
|
1560
|
+
// manual key entry, and a `widget.<id>.<key>` override still wins per
|
|
1561
|
+
// instance. No existing hook, primitive, manifest field, or token changed
|
|
1562
|
+
// shape — minor bump on the pre-1.0 channel.
|
|
1563
|
+
version: "1.28.0",
|
|
1564
|
+
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
1490
1565
|
hooks: HOOKS,
|
|
1491
1566
|
primitives: PRIMITIVES,
|
|
1492
1567
|
manifestSchema: MANIFEST_SCHEMA,
|
|
@@ -1524,4 +1599,8 @@ module.exports = {
|
|
|
1524
1599
|
requiredContextKeys,
|
|
1525
1600
|
widgetTranslationPrefix,
|
|
1526
1601
|
widgetTranslationKey,
|
|
1602
|
+
sharedTranslationPrefix,
|
|
1603
|
+
sharedTranslationKey,
|
|
1604
|
+
isSharedTranslationKey,
|
|
1605
|
+
SHARED_TRANSLATION_KEYS,
|
|
1527
1606
|
};
|
package/dist/contract.js
CHANGED
|
@@ -1285,6 +1285,65 @@ function widgetTranslationKey(id, key) {
|
|
|
1285
1285
|
return `widget.${id}.${key}`;
|
|
1286
1286
|
}
|
|
1287
1287
|
|
|
1288
|
+
// REQ-L10N-SHARED — predefined SHARED translation keys for the standard
|
|
1289
|
+
// strings the default widgets repeat ("Submit", "Cancel", "Loading…", …).
|
|
1290
|
+
// They live in ONE tenant-wide namespace (`shared.<key>`) rather than the
|
|
1291
|
+
// per-widget namespace, so identical strings translate ONCE and every widget
|
|
1292
|
+
// that calls `t("<sharedKey>")` resolves the same dictionary value. An author
|
|
1293
|
+
// still overrides any instance by setting `widget.<id>.<key>` in the
|
|
1294
|
+
// Translations admin — `useI18n` resolves the per-widget key FIRST, then the
|
|
1295
|
+
// shared key, then the raw key, so the override is per-instance.
|
|
1296
|
+
//
|
|
1297
|
+
// This is the analogue of `widget.<id>.` for cross-widget reuse: one prefix,
|
|
1298
|
+
// one definition (the hook reads + the backend seeder writes both call these),
|
|
1299
|
+
// so the wire key and the lookup key can never drift.
|
|
1300
|
+
const SHARED_TRANSLATION_PREFIX = "shared.";
|
|
1301
|
+
function sharedTranslationPrefix() {
|
|
1302
|
+
return SHARED_TRANSLATION_PREFIX;
|
|
1303
|
+
}
|
|
1304
|
+
function sharedTranslationKey(key) {
|
|
1305
|
+
return `${SHARED_TRANSLATION_PREFIX}${key}`;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// The predefined set. Map of bare key -> { en } (English default only; the
|
|
1309
|
+
// tenant adds target-language values once in the Translations admin and every
|
|
1310
|
+
// widget inherits them). Adding/removing a key is a CONTRACT change (it grows
|
|
1311
|
+
// the dictionary every tenant gets), so the list lives with the contract and
|
|
1312
|
+
// the seeder reads it from here — never a second copy.
|
|
1313
|
+
const SHARED_TRANSLATION_KEYS = Object.freeze({
|
|
1314
|
+
submit: { en: "Submit" },
|
|
1315
|
+
cancel: { en: "Cancel" },
|
|
1316
|
+
save: { en: "Save" },
|
|
1317
|
+
delete: { en: "Delete" },
|
|
1318
|
+
edit: { en: "Edit" },
|
|
1319
|
+
close: { en: "Close" },
|
|
1320
|
+
confirm: { en: "Confirm" },
|
|
1321
|
+
back: { en: "Back" },
|
|
1322
|
+
next: { en: "Next" },
|
|
1323
|
+
previous: { en: "Previous" },
|
|
1324
|
+
yes: { en: "Yes" },
|
|
1325
|
+
no: { en: "No" },
|
|
1326
|
+
ok: { en: "OK" },
|
|
1327
|
+
loading: { en: "Loading…" },
|
|
1328
|
+
search: { en: "Search" },
|
|
1329
|
+
no_results: { en: "No results" },
|
|
1330
|
+
required: { en: "Required" },
|
|
1331
|
+
retry: { en: "Retry" },
|
|
1332
|
+
add: { en: "Add" },
|
|
1333
|
+
remove: { en: "Remove" },
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1336
|
+
// True when `key` is one of the predefined shared keys — the discriminator
|
|
1337
|
+
// `useI18n` uses to decide whether to try the `shared.<key>` namespace. A key
|
|
1338
|
+
// the author invents is NOT shared (it stays per-widget) so authors can't
|
|
1339
|
+
// accidentally collide with another widget by reusing a bare name.
|
|
1340
|
+
function isSharedTranslationKey(key) {
|
|
1341
|
+
return (
|
|
1342
|
+
typeof key === "string" &&
|
|
1343
|
+
Object.prototype.hasOwnProperty.call(SHARED_TRANSLATION_KEYS, key)
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1288
1347
|
const CONTRACT = deepFreeze({
|
|
1289
1348
|
// REQ-WSDK-PLATFORM bump:
|
|
1290
1349
|
// - `vettedImports` is a new field (rich allowlist with platforms +
|
|
@@ -1486,7 +1545,23 @@ const CONTRACT = deepFreeze({
|
|
|
1486
1545
|
// on `react/jsx-runtime`. Adding them converges the linter onto the same
|
|
1487
1546
|
// contract every other surface already honoured (CLAUDE.md §3). No
|
|
1488
1547
|
// existing entry changed shape — minor bump on the pre-1.0 channel.
|
|
1489
|
-
|
|
1548
|
+
//
|
|
1549
|
+
// 1.28.0: additive (REQ-L10N-SHARED) — predefined SHARED translation keys.
|
|
1550
|
+
// A new tenant-wide namespace `shared.<key>` carries the standard strings
|
|
1551
|
+
// the default widgets repeat ("Submit", "Cancel", "Loading…", …) so an
|
|
1552
|
+
// identical string is translated ONCE and every widget that calls
|
|
1553
|
+
// `t("<sharedKey>")` inherits it. New contract field
|
|
1554
|
+
// `sharedTranslationKeys` (the predefined map, the single source the
|
|
1555
|
+
// backend seeder reads) + new exported helpers `sharedTranslationPrefix()`
|
|
1556
|
+
// / `sharedTranslationKey(key)` / `isSharedTranslationKey(key)`.
|
|
1557
|
+
// `useI18n().t(key)` now resolves the per-widget key first, THEN the
|
|
1558
|
+
// shared key (when `key` is one of the predefined shared keys), then the
|
|
1559
|
+
// raw key — so a default widget picks up the shared translation with no
|
|
1560
|
+
// manual key entry, and a `widget.<id>.<key>` override still wins per
|
|
1561
|
+
// instance. No existing hook, primitive, manifest field, or token changed
|
|
1562
|
+
// shape — minor bump on the pre-1.0 channel.
|
|
1563
|
+
version: "1.28.0",
|
|
1564
|
+
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
1490
1565
|
hooks: HOOKS,
|
|
1491
1566
|
primitives: PRIMITIVES,
|
|
1492
1567
|
manifestSchema: MANIFEST_SCHEMA,
|
|
@@ -1524,4 +1599,8 @@ export {
|
|
|
1524
1599
|
requiredContextKeys,
|
|
1525
1600
|
widgetTranslationPrefix,
|
|
1526
1601
|
widgetTranslationKey,
|
|
1602
|
+
sharedTranslationPrefix,
|
|
1603
|
+
sharedTranslationKey,
|
|
1604
|
+
isSharedTranslationKey,
|
|
1605
|
+
SHARED_TRANSLATION_KEYS,
|
|
1527
1606
|
};
|
package/dist/hooks.js
CHANGED
|
@@ -26,10 +26,18 @@ import React, {
|
|
|
26
26
|
useRef,
|
|
27
27
|
useState,
|
|
28
28
|
} from "react";
|
|
29
|
-
// REQ-L10N-WIDGET: the
|
|
30
|
-
// (contract.js). useI18n (lookup) and the backend seeder (write) both
|
|
31
|
-
// so the namespaced
|
|
32
|
-
|
|
29
|
+
// REQ-L10N-WIDGET / REQ-L10N-SHARED: the translation key formats live in ONE
|
|
30
|
+
// place (contract.js). useI18n (lookup) and the backend seeder (write) both
|
|
31
|
+
// call them so the namespaced keys can never drift between the two sides.
|
|
32
|
+
// `widgetTranslationKey` builds the per-widget key (`widget.<id>.<key>`);
|
|
33
|
+
// `sharedTranslationKey` builds the tenant-wide predefined key
|
|
34
|
+
// (`shared.<key>`); `isSharedTranslationKey` tells the hook whether a bare key
|
|
35
|
+
// is one of the predefined shared keys and so should try the shared namespace.
|
|
36
|
+
import {
|
|
37
|
+
widgetTranslationKey,
|
|
38
|
+
sharedTranslationKey,
|
|
39
|
+
isSharedTranslationKey,
|
|
40
|
+
} from "./contract.js";
|
|
33
41
|
|
|
34
42
|
/** @internal — host-injected context value of shape WidgetContext (see index.d.ts). */
|
|
35
43
|
const HostWidgetContext = createContext(null);
|
|
@@ -240,7 +248,27 @@ export function useI18n() {
|
|
|
240
248
|
return scoped;
|
|
241
249
|
}
|
|
242
250
|
}
|
|
243
|
-
// 2)
|
|
251
|
+
// 2) REQ-L10N-SHARED: for a predefined SHARED key, try the tenant-wide
|
|
252
|
+
// `shared.<key>` namespace next. This is what lets identical
|
|
253
|
+
// default-widget strings ("Submit", "Cancel", …) translate ONCE: a
|
|
254
|
+
// widget that has no per-instance `widget.<id>.<key>` override
|
|
255
|
+
// inherits the shared value. The per-widget step above runs FIRST,
|
|
256
|
+
// so an author override of a single instance still wins for that
|
|
257
|
+
// instance. Only the predefined keys are tried here — an
|
|
258
|
+
// author-invented bare key is never silently shared.
|
|
259
|
+
if (isSharedTranslationKey(key)) {
|
|
260
|
+
const shared = sharedTranslationKey(key);
|
|
261
|
+
const scoped = hostT(shared);
|
|
262
|
+
if (
|
|
263
|
+
typeof scoped === "string" &&
|
|
264
|
+
scoped.length > 0 &&
|
|
265
|
+
scoped !== shared &&
|
|
266
|
+
!scoped.startsWith("{{t:")
|
|
267
|
+
) {
|
|
268
|
+
return scoped;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// 3) Fall back to the raw key (shared app keys + widgets with no
|
|
244
272
|
// manifest translations — unchanged from pre-1.10 behaviour).
|
|
245
273
|
const out = hostT(key, fallback);
|
|
246
274
|
if (typeof out === "string" && out.length > 0 && out !== key) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.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",
|