@colixsystems/widget-sdk 0.197.0 → 0.199.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 +148 -3
- package/dist/avatar-color.js +35 -0
- package/dist/bundled-assets-fs.js +60 -0
- package/dist/bundled-assets.cjs +239 -0
- package/dist/bundled-assets.js +239 -0
- package/dist/cli.js +52 -11
- package/dist/contract.cjs +225 -26
- package/dist/contract.js +225 -26
- package/dist/detail-parts-view.js +9 -4
- package/dist/devserver.js +68 -10
- package/dist/hooks.js +549 -28
- package/dist/index.d.ts +247 -5
- package/dist/index.js +14 -0
- package/dist/index.native.js +14 -0
- package/dist/initials.js +3 -2
- package/dist/linter.cjs +28 -0
- package/dist/linter.js +28 -0
- package/dist/manifest.cjs +3 -1
- package/dist/manifest.js +3 -1
- package/dist/overlay-view.js +2 -5
- package/dist/overlay.js +12 -5
- package/dist/primitives.js +2 -0
- package/dist/primitives.native.js +2 -0
- package/dist/video.js +51 -0
- package/dist/video.native.js +61 -0
- package/dist/web-modal.js +139 -0
- package/package.json +11 -1
- package/src/contract.js +225 -26
package/dist/index.d.ts
CHANGED
|
@@ -187,6 +187,12 @@ export interface WidgetDatastoreTemplateColumn {
|
|
|
187
187
|
relationType?: "ONE_TO_ONE" | "ONE_TO_MANY" | "MANY_TO_MANY";
|
|
188
188
|
/** REQ-ACL-RELINHERIT: opt this RELATION column into row-level ACL inheritance. */
|
|
189
189
|
inheritAcl?: boolean;
|
|
190
|
+
/**
|
|
191
|
+
* With `inheritAcl: true` only: rows inherit read access alone. Members of
|
|
192
|
+
* the parent row read every child and may add children, but change or
|
|
193
|
+
* delete only the rows they created (pair with `grantCreatorPermissions`).
|
|
194
|
+
*/
|
|
195
|
+
inheritAclReadOnly?: boolean;
|
|
190
196
|
/**
|
|
191
197
|
* ONE_TO_ONE / ONE_TO_MANY only: what deleting the referenced row does to
|
|
192
198
|
* this table's rows. `CASCADE` deletes them too; `SET_NULL` (the default)
|
|
@@ -202,6 +208,26 @@ export interface WidgetDatastoreTemplateColumn {
|
|
|
202
208
|
* the backend must resolve); publishing one is rejected.
|
|
203
209
|
*/
|
|
204
210
|
encrypted?: boolean;
|
|
211
|
+
/**
|
|
212
|
+
* Written when a create omits the column. `"@currentUser"` (USER) stamps
|
|
213
|
+
* the signed-in user and `"@now"` (DATE / DATETIME) the current date or
|
|
214
|
+
* time. Other types take a literal of their own type. FILE, RELATION and
|
|
215
|
+
* USER_GROUP columns take none, because ids belong to the installing
|
|
216
|
+
* workspace.
|
|
217
|
+
*/
|
|
218
|
+
defaultValue?: unknown;
|
|
219
|
+
/**
|
|
220
|
+
* With a `"@currentUser"` or `"@now"` default only: the server always
|
|
221
|
+
* stamps it. Any value a client sends for the column is ignored on create
|
|
222
|
+
* and on update, so an author column cannot be forged.
|
|
223
|
+
*/
|
|
224
|
+
defaultLocked?: boolean;
|
|
225
|
+
/**
|
|
226
|
+
* Write-once: a create sets the column as usual (the value sent, or the
|
|
227
|
+
* default), and any value sent for it on update is ignored. Not valid on a
|
|
228
|
+
* `MANY_TO_MANY` relation, whose links live outside the row.
|
|
229
|
+
*/
|
|
230
|
+
immutable?: boolean;
|
|
205
231
|
}
|
|
206
232
|
|
|
207
233
|
export interface WidgetDatastoreTemplateTable {
|
|
@@ -367,7 +393,7 @@ export interface WidgetManifest {
|
|
|
367
393
|
* under a per-widget namespace (`widget.<id>.<key>`), so `useI18n().t(key)`
|
|
368
394
|
* resolves the namespaced key automatically — the author never types the
|
|
369
395
|
* prefix. Non-destructive (admin edits win; absent languages are not
|
|
370
|
-
* created) and persists across uninstalls. Caps: ≤
|
|
396
|
+
* created) and persists across uninstalls. Caps: ≤200 keys, key matches
|
|
371
397
|
* /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/, value ≤1 KB.
|
|
372
398
|
*/
|
|
373
399
|
translations?: Record<string, { en: string } & Record<string, string>>;
|
|
@@ -639,11 +665,46 @@ export interface IdentificationClient {
|
|
|
639
665
|
*/
|
|
640
666
|
export interface NotificationsClient {
|
|
641
667
|
send(body: SendNotificationRequest): Promise<NotificationRow>;
|
|
668
|
+
/**
|
|
669
|
+
* sc-10268 — the signed-in user's per-record mutes; backs `useRecordMute`
|
|
670
|
+
* and `useRecordMutes`.
|
|
671
|
+
* Optional: a host without it leaves the hook reading not muted.
|
|
672
|
+
*/
|
|
673
|
+
mutes?: {
|
|
674
|
+
get(ref: { table_id: string; record_id: string }): Promise<RecordMuteWire>;
|
|
675
|
+
set(body: {
|
|
676
|
+
table_id: string;
|
|
677
|
+
record_id: string;
|
|
678
|
+
muted_until: string | null;
|
|
679
|
+
}): Promise<RecordMuteWire>;
|
|
680
|
+
clear(ref: { table_id: string; record_id: string }): Promise<void>;
|
|
681
|
+
/** Backs `useRecordMutes`: one page of the user's active mutes. */
|
|
682
|
+
list(query: {
|
|
683
|
+
table_id: string;
|
|
684
|
+
limit?: number;
|
|
685
|
+
offset?: number;
|
|
686
|
+
}): Promise<{
|
|
687
|
+
data: Array<{ record_id: string; muted_until: string | null }>;
|
|
688
|
+
meta: { total: number; limit: number; offset: number };
|
|
689
|
+
}>;
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/** The mute wire shape, snake_case verbatim. */
|
|
694
|
+
export interface RecordMuteWire {
|
|
695
|
+
muted: boolean;
|
|
696
|
+
muted_until: string | null;
|
|
642
697
|
}
|
|
643
698
|
|
|
644
699
|
export interface WidgetContext<TProps = unknown> {
|
|
645
700
|
props: TProps;
|
|
646
|
-
widget: {
|
|
701
|
+
widget: {
|
|
702
|
+
id: string;
|
|
703
|
+
instanceId: string;
|
|
704
|
+
version: string;
|
|
705
|
+
/** Bundled file name → absolute URL; `{}` when the package bundles nothing. */
|
|
706
|
+
assets: Record<string, string>;
|
|
707
|
+
};
|
|
647
708
|
/**
|
|
648
709
|
* REQ-LAY-08 — optional host layout hint backing `useFill()`. `true` when
|
|
649
710
|
* the host sized this widget to fill its layout slot's available height (a
|
|
@@ -988,6 +1049,11 @@ export interface DirectoryQuery {
|
|
|
988
1049
|
* `useGroups()` row. Pair with a group picker to build "members of group X".
|
|
989
1050
|
*/
|
|
990
1051
|
group_id?: string;
|
|
1052
|
+
/**
|
|
1053
|
+
* Only these users (1 to 100 ids): resolve the names behind ids you already
|
|
1054
|
+
* hold — message senders, members — without loading the whole roster.
|
|
1055
|
+
*/
|
|
1056
|
+
ids?: string[];
|
|
991
1057
|
limit?: number;
|
|
992
1058
|
offset?: number;
|
|
993
1059
|
}
|
|
@@ -1145,6 +1211,45 @@ export interface SendNotificationApi {
|
|
|
1145
1211
|
*/
|
|
1146
1212
|
export function useSendNotification(): SendNotificationApi;
|
|
1147
1213
|
|
|
1214
|
+
export interface RecordMuteApi {
|
|
1215
|
+
muted: boolean;
|
|
1216
|
+
/** ISO time the mute ends; null when muted until unmuted, or not muted. */
|
|
1217
|
+
mutedUntil: string | null;
|
|
1218
|
+
loading: boolean;
|
|
1219
|
+
error: NotificationError | null;
|
|
1220
|
+
/** An ISO time or a Date; null or omitted mutes until `unmute()`. */
|
|
1221
|
+
mute(until?: string | Date | null): Promise<void>;
|
|
1222
|
+
unmute(): Promise<void>;
|
|
1223
|
+
refetch(): Promise<void>;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
/**
|
|
1227
|
+
* sc-10268 — the signed-in user's mute of one datastore record's
|
|
1228
|
+
* notifications. While muted, `notifications.notifyRecordSubjects` on the
|
|
1229
|
+
* record sends them nothing. A record they cannot read fails with
|
|
1230
|
+
* `RECORD_NOT_FOUND`; a falsy id returns a stable not-muted result.
|
|
1231
|
+
*/
|
|
1232
|
+
export function useRecordMute(
|
|
1233
|
+
tableId: string | null | undefined,
|
|
1234
|
+
recordId: string | null | undefined,
|
|
1235
|
+
): RecordMuteApi;
|
|
1236
|
+
|
|
1237
|
+
export interface RecordMutesApi {
|
|
1238
|
+
/** True while the record's mute is active; false once a timed mute lapses. */
|
|
1239
|
+
isMuted(recordId: string | null | undefined): boolean;
|
|
1240
|
+
loading: boolean;
|
|
1241
|
+
error: NotificationError | null;
|
|
1242
|
+
refetch(): Promise<void>;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
/**
|
|
1246
|
+
* sc-10268 — which records of one table the signed-in user has muted, in one
|
|
1247
|
+
* read for a whole list. A `useRecordMute` write on the table shows at once.
|
|
1248
|
+
*/
|
|
1249
|
+
export function useRecordMutes(
|
|
1250
|
+
tableId: string | null | undefined,
|
|
1251
|
+
): RecordMutesApi;
|
|
1252
|
+
|
|
1148
1253
|
export interface WorkspaceCurrency {
|
|
1149
1254
|
/** The ISO code this workspace charges its app users in. */
|
|
1150
1255
|
currency: string;
|
|
@@ -1515,6 +1620,18 @@ export function useAsset(assetId: string | null | undefined): {
|
|
|
1515
1620
|
refetch(): Promise<void>;
|
|
1516
1621
|
};
|
|
1517
1622
|
|
|
1623
|
+
/**
|
|
1624
|
+
* URL of an image or sound file the widget package ships in its `assets/`
|
|
1625
|
+
* directory, or `null` when the package has no file with that name.
|
|
1626
|
+
*
|
|
1627
|
+
* ```jsx
|
|
1628
|
+
* const logo = useBundledAsset("logo.png");
|
|
1629
|
+
* const chime = useAudioPlayer(useBundledAsset("chime.mp3"));
|
|
1630
|
+
* return logo ? <Image source={{ uri: logo }} /> : null;
|
|
1631
|
+
* ```
|
|
1632
|
+
*/
|
|
1633
|
+
export function useBundledAsset(fileName: string | null | undefined): string | null;
|
|
1634
|
+
|
|
1518
1635
|
/**
|
|
1519
1636
|
* List every tenant asset carrying a given tag. Reads `ctx.assets.list({
|
|
1520
1637
|
* tag, type, limit })` and unwraps the `{ data, meta }` envelope.
|
|
@@ -1704,6 +1821,35 @@ export function useNavigation(): {
|
|
|
1704
1821
|
*/
|
|
1705
1822
|
export function useRouteParams(): Record<string, unknown>;
|
|
1706
1823
|
|
|
1824
|
+
/**
|
|
1825
|
+
* sc-10268 — puts `count` on the menu entry or tab of the page this widget is
|
|
1826
|
+
* on. The host sums the page's widgets and keeps the last count after the
|
|
1827
|
+
* widget leaves the screen; 0 clears this widget's share.
|
|
1828
|
+
*/
|
|
1829
|
+
/**
|
|
1830
|
+
* sc-10268 — `{ uri }` an `<Image>` can show for a picked file before it is
|
|
1831
|
+
* uploaded. Null for no file, or a host that cannot preview one.
|
|
1832
|
+
*/
|
|
1833
|
+
export function useFilePreview(file: unknown): { uri: string | null };
|
|
1834
|
+
|
|
1835
|
+
export function usePageBadge(count: number | null | undefined): {
|
|
1836
|
+
supported: boolean;
|
|
1837
|
+
};
|
|
1838
|
+
|
|
1839
|
+
/**
|
|
1840
|
+
* sc-10268 — Android's back button for a widget with pages of its own.
|
|
1841
|
+
* `handler` runs on a back press while the widget's screen is in view; return
|
|
1842
|
+
* true when it went back a step, false to let the app go back. Pass null while
|
|
1843
|
+
* there is nothing to go back from. `supported` is false on the web, which has
|
|
1844
|
+
* no hardware back, so keep a visible back control too.
|
|
1845
|
+
*
|
|
1846
|
+
* @example
|
|
1847
|
+
* useHardwareBack(openThread ? () => { closeThread(); return true; } : null);
|
|
1848
|
+
*/
|
|
1849
|
+
export function useHardwareBack(handler: (() => boolean) | null | undefined): {
|
|
1850
|
+
supported: boolean;
|
|
1851
|
+
};
|
|
1852
|
+
|
|
1707
1853
|
/**
|
|
1708
1854
|
* REQ-NAV-05 — the PAGE's resolved context. When the page declares typed
|
|
1709
1855
|
* parameters, the host resolves them ONCE before any widget renders: `params`
|
|
@@ -2068,7 +2214,7 @@ export interface CameraOptions {
|
|
|
2068
2214
|
quality?: number;
|
|
2069
2215
|
}
|
|
2070
2216
|
|
|
2071
|
-
/** A photo taken or picked through `useCamera()`, normalised across hosts. */
|
|
2217
|
+
/** A photo (or, from `pick({ videos: true })`, a video) taken or picked through `useCamera()`, normalised across hosts. */
|
|
2072
2218
|
export interface CameraAsset {
|
|
2073
2219
|
/** Displayable source — `<Image source={{ uri }} />` / `<img src>`. */
|
|
2074
2220
|
uri: string;
|
|
@@ -2079,6 +2225,8 @@ export interface CameraAsset {
|
|
|
2079
2225
|
height: number | null;
|
|
2080
2226
|
/** Bytes, when the host reports it. */
|
|
2081
2227
|
size: number | null;
|
|
2228
|
+
/** A video's length in milliseconds when the host reports it; null for a photo. */
|
|
2229
|
+
durationMs: number | null;
|
|
2082
2230
|
/**
|
|
2083
2231
|
* Ready-to-upload part — a Blob on both hosts: a `File` on web,
|
|
2084
2232
|
* expo-file-system's `File` on native. Expo's fetch rejects any other shape.
|
|
@@ -2096,8 +2244,13 @@ export interface CameraResult {
|
|
|
2096
2244
|
supported: boolean;
|
|
2097
2245
|
/** Open the camera. Resolves null if the user dismisses it. */
|
|
2098
2246
|
capture(): Promise<CameraAsset | null>;
|
|
2099
|
-
/**
|
|
2100
|
-
|
|
2247
|
+
/**
|
|
2248
|
+
* Open the photo library. Resolves null if the user dismisses it.
|
|
2249
|
+
* `{ multiple: true }` resolves an array of up to 10; `{ videos: true }`
|
|
2250
|
+
* offers videos beside photos (play one with `<Video>`).
|
|
2251
|
+
*/
|
|
2252
|
+
pick(options: { multiple: true; videos?: boolean }): Promise<CameraAsset[] | null>;
|
|
2253
|
+
pick(options?: { multiple?: false; videos?: boolean }): Promise<CameraAsset | null>;
|
|
2101
2254
|
/** Clear `asset` and `error`, releasing the held asset. */
|
|
2102
2255
|
reset(): void;
|
|
2103
2256
|
}
|
|
@@ -2412,6 +2565,10 @@ export class NotificationError extends Error {
|
|
|
2412
2565
|
| "FORBIDDEN"
|
|
2413
2566
|
| "RECIPIENT_NOT_FOUND"
|
|
2414
2567
|
| "RATE_LIMITED"
|
|
2568
|
+
| "RECORD_NOT_FOUND"
|
|
2569
|
+
| "INVALID_MUTED_UNTIL"
|
|
2570
|
+
| "INVALID_RECORD_REF"
|
|
2571
|
+
| "NOTIFICATIONS_UNAVAILABLE"
|
|
2415
2572
|
| "INTERNAL"
|
|
2416
2573
|
| string;
|
|
2417
2574
|
constructor(
|
|
@@ -2904,6 +3061,35 @@ export const Gradient: (props: {
|
|
|
2904
3061
|
children?: ReactNode;
|
|
2905
3062
|
}) => any;
|
|
2906
3063
|
|
|
3064
|
+
/**
|
|
3065
|
+
* sc-10268 — plays a video file inline with the platform's own controls, the
|
|
3066
|
+
* first frame showing before playback. Web uses the browser's `<video>`;
|
|
3067
|
+
* native uses expo-video's VideoView. It fills the box `style` gives it.
|
|
3068
|
+
*
|
|
3069
|
+
* @example
|
|
3070
|
+
* <Video
|
|
3071
|
+
* source={file.url}
|
|
3072
|
+
* contentFit="cover"
|
|
3073
|
+
* style={{ width: 240, height: 180, borderRadius: theme.radii.md, overflow: "hidden" }}
|
|
3074
|
+
* onError={() => setPlayable(false)}
|
|
3075
|
+
* />
|
|
3076
|
+
*/
|
|
3077
|
+
export const Video: (props: {
|
|
3078
|
+
/** A playable URL; null renders an empty box. */
|
|
3079
|
+
source: string | null;
|
|
3080
|
+
/** Play, seek and fullscreen controls. Defaults to true. */
|
|
3081
|
+
controls?: boolean;
|
|
3082
|
+
/** Starts playing muted, as browsers allow only muted autoplay. */
|
|
3083
|
+
autoPlay?: boolean;
|
|
3084
|
+
/** Letterboxed ("contain", the default) or cropped to fill ("cover"). */
|
|
3085
|
+
contentFit?: "contain" | "cover";
|
|
3086
|
+
style?: any;
|
|
3087
|
+
accessibilityLabel?: string;
|
|
3088
|
+
testID?: string;
|
|
3089
|
+
/** The file could not be played on this host. */
|
|
3090
|
+
onError?: () => void;
|
|
3091
|
+
}) => any;
|
|
3092
|
+
|
|
2907
3093
|
/**
|
|
2908
3094
|
* sc-6607 — screen-level overlay. Renders OUTSIDE the widget's layout box on
|
|
2909
3095
|
* both hosts (web portals it to the document root, native uses the OS modal),
|
|
@@ -3054,9 +3240,14 @@ export const Badge: (props: {
|
|
|
3054
3240
|
}) => any;
|
|
3055
3241
|
|
|
3056
3242
|
/** Themed picture of a person, falling back to initials, then a glyph. */
|
|
3243
|
+
/** The stable colour `<Avatar seed>` paints for a seed, or null for none. */
|
|
3244
|
+
export function avatarColorFor(seed: string | null | undefined): string | null;
|
|
3245
|
+
|
|
3057
3246
|
export const Avatar: (props: {
|
|
3058
3247
|
/** Drives the initials and the accessibility label. */
|
|
3059
3248
|
name?: string;
|
|
3249
|
+
/** A stable id (a user id): tints the monogram with that person's colour. */
|
|
3250
|
+
seed?: string;
|
|
3060
3251
|
/** An absolute image URL. */
|
|
3061
3252
|
url?: string;
|
|
3062
3253
|
/** A size token, or a number of px (clamped to 16-160). */
|
|
@@ -3532,6 +3723,12 @@ export interface LintFinding {
|
|
|
3532
3723
|
}
|
|
3533
3724
|
export interface LintOptions {
|
|
3534
3725
|
manifest?: { requestedScopes?: string[]; supportedPlatforms?: string[] };
|
|
3726
|
+
/**
|
|
3727
|
+
* File names in the package's `assets/` directory. When given, a
|
|
3728
|
+
* `useBundledAsset("name")` literal naming a file outside the list is a
|
|
3729
|
+
* `bundled-asset-missing` error; omit it to skip that rule.
|
|
3730
|
+
*/
|
|
3731
|
+
assetFiles?: string[];
|
|
3535
3732
|
}
|
|
3536
3733
|
export function lintSource(
|
|
3537
3734
|
source: string,
|
|
@@ -3541,6 +3738,40 @@ export function lintSource(
|
|
|
3541
3738
|
findings: LintFinding[];
|
|
3542
3739
|
};
|
|
3543
3740
|
|
|
3741
|
+
// Bundled assets — the files a package ships in its `assets/` directory.
|
|
3742
|
+
export type BundledAssetKind = "image" | "audio";
|
|
3743
|
+
export type BundledAssetErrorCode =
|
|
3744
|
+
| "ASSET_NAME_INVALID"
|
|
3745
|
+
| "ASSET_TYPE_NOT_ALLOWED"
|
|
3746
|
+
| "ASSET_EMPTY"
|
|
3747
|
+
| "ASSET_CONTENT_MISMATCH"
|
|
3748
|
+
| "ASSET_NAME_DUPLICATE"
|
|
3749
|
+
| "ASSET_TOO_MANY";
|
|
3750
|
+
export interface BundledAssetError {
|
|
3751
|
+
code: BundledAssetErrorCode;
|
|
3752
|
+
/** `null` for an error about the directory as a whole (`ASSET_TOO_MANY`). */
|
|
3753
|
+
name: string | null;
|
|
3754
|
+
message: string;
|
|
3755
|
+
}
|
|
3756
|
+
export interface BundledAsset {
|
|
3757
|
+
name: string;
|
|
3758
|
+
kind: BundledAssetKind;
|
|
3759
|
+
mimeType: string;
|
|
3760
|
+
sizeBytes: number;
|
|
3761
|
+
}
|
|
3762
|
+
/** The package directory bundled files live in (`"assets"`); from the `./bundled-assets` entry. */
|
|
3763
|
+
export const BUNDLED_ASSET_DIRECTORY: string;
|
|
3764
|
+
/** True when `name` may be used as a file name inside `assets/`. */
|
|
3765
|
+
export function isValidBundledAssetName(name: unknown): boolean;
|
|
3766
|
+
/** Identifies a bundled file from its leading bytes; `null` when it is not an allowed type. */
|
|
3767
|
+
export function sniffBundledAssetType(
|
|
3768
|
+
bytes: Uint8Array,
|
|
3769
|
+
): { kind: BundledAssetKind; mimeType: string; extensions: string[] } | null;
|
|
3770
|
+
/** Validates the files of an `assets/` directory; `assets` is sorted by name. */
|
|
3771
|
+
export function validateBundledAssets(
|
|
3772
|
+
files: Array<{ name: string; bytes: Uint8Array }>,
|
|
3773
|
+
): { ok: boolean; errors: BundledAssetError[]; assets: BundledAsset[] };
|
|
3774
|
+
|
|
3544
3775
|
// --------------------------------------------------------------- CONTRACT
|
|
3545
3776
|
//
|
|
3546
3777
|
// Single-source-of-truth contract artefact. See docs/design/ai-widget-contract.md.
|
|
@@ -3595,6 +3826,17 @@ export interface AiWidgetContract {
|
|
|
3595
3826
|
readonly manifestSchema: Readonly<Record<string, ContractManifestField>>;
|
|
3596
3827
|
readonly manifestCategories: ReadonlyArray<string>;
|
|
3597
3828
|
readonly manifestPlatforms: ReadonlyArray<string>;
|
|
3829
|
+
/** Largest package a widget may ship, bundled files included. */
|
|
3830
|
+
readonly packageMaxBytes: number;
|
|
3831
|
+
/** What a package may ship in its `assets/` directory. */
|
|
3832
|
+
readonly bundledAssets: {
|
|
3833
|
+
readonly directory: string;
|
|
3834
|
+
readonly maxFiles: number;
|
|
3835
|
+
readonly maxNameLength: number;
|
|
3836
|
+
readonly types: Readonly<
|
|
3837
|
+
Record<BundledAssetKind, { readonly extensions: ReadonlyArray<string> }>
|
|
3838
|
+
>;
|
|
3839
|
+
};
|
|
3598
3840
|
readonly themeTokens: ThemeTokens;
|
|
3599
3841
|
/** sc-7800 — the brand colour tokens a `type: "color"` field may bind to. */
|
|
3600
3842
|
readonly themeColorTokens: ReadonlyArray<{ path: string; label: string }>;
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ export {
|
|
|
39
39
|
useAgent,
|
|
40
40
|
useAsset,
|
|
41
41
|
useAssetsByTag,
|
|
42
|
+
useBundledAsset,
|
|
42
43
|
useFilestoreFiles,
|
|
43
44
|
useFilestoreFile,
|
|
44
45
|
useFilestoreUpload,
|
|
@@ -46,6 +47,7 @@ export {
|
|
|
46
47
|
useFilestoreFolders,
|
|
47
48
|
useFilestoreCreateFolder,
|
|
48
49
|
useFilestoreCopy,
|
|
50
|
+
useFilestoreDelete,
|
|
49
51
|
useFileSignature,
|
|
50
52
|
useFileSignatures,
|
|
51
53
|
useFileRoster,
|
|
@@ -66,6 +68,8 @@ export {
|
|
|
66
68
|
usePayments,
|
|
67
69
|
useWorkspaceCurrency,
|
|
68
70
|
useSendNotification,
|
|
71
|
+
useRecordMute,
|
|
72
|
+
useRecordMutes,
|
|
69
73
|
useTheme,
|
|
70
74
|
useWidgetStyle,
|
|
71
75
|
useI18n,
|
|
@@ -78,6 +82,9 @@ export {
|
|
|
78
82
|
useFill,
|
|
79
83
|
useNavigation,
|
|
80
84
|
useRouteParams,
|
|
85
|
+
usePageBadge,
|
|
86
|
+
useHardwareBack,
|
|
87
|
+
useFilePreview,
|
|
81
88
|
usePageContext,
|
|
82
89
|
useWidgetRoute,
|
|
83
90
|
useChildRenderer,
|
|
@@ -160,8 +167,14 @@ export {
|
|
|
160
167
|
DateTimePicker,
|
|
161
168
|
FilePicker,
|
|
162
169
|
Gradient,
|
|
170
|
+
Video,
|
|
163
171
|
} from "./primitives.js";
|
|
164
172
|
export { lintSource, bannedIdentifiers } from "./linter.js";
|
|
173
|
+
export {
|
|
174
|
+
validateBundledAssets,
|
|
175
|
+
sniffBundledAssetType,
|
|
176
|
+
isValidBundledAssetName,
|
|
177
|
+
} from "./bundled-assets.js";
|
|
165
178
|
// sc-3696: the trailing six are the theme-derivation helpers both hosts resolve
|
|
166
179
|
// `useTheme()` with (the Player through widgetTheme.js, the export through its
|
|
167
180
|
// generated theme module), plus the gradient angle projection `<Gradient>`
|
|
@@ -215,6 +228,7 @@ export { List, ListRow, ListHeader, EmptyState } from "./list.js";
|
|
|
215
228
|
// Themed detail parts; the stock Badge/Avatar/Progress/Stat/Divider wrap these.
|
|
216
229
|
export { Badge, Avatar, Progress, Stat, Divider } from "./detail-parts.js";
|
|
217
230
|
export { initialsFor } from "./initials.js";
|
|
231
|
+
export { avatarColorFor } from "./avatar-color.js";
|
|
218
232
|
// The one card surface: the theme's `card` scope, a gradient fill, margin.
|
|
219
233
|
export { Card } from "./card.js";
|
|
220
234
|
export {
|
package/dist/index.native.js
CHANGED
|
@@ -39,6 +39,7 @@ export {
|
|
|
39
39
|
useAgent,
|
|
40
40
|
useAsset,
|
|
41
41
|
useAssetsByTag,
|
|
42
|
+
useBundledAsset,
|
|
42
43
|
useFilestoreFiles,
|
|
43
44
|
useFilestoreFile,
|
|
44
45
|
useFilestoreUpload,
|
|
@@ -46,6 +47,7 @@ export {
|
|
|
46
47
|
useFilestoreFolders,
|
|
47
48
|
useFilestoreCreateFolder,
|
|
48
49
|
useFilestoreCopy,
|
|
50
|
+
useFilestoreDelete,
|
|
49
51
|
useFileSignature,
|
|
50
52
|
useFileSignatures,
|
|
51
53
|
useFileRoster,
|
|
@@ -66,6 +68,8 @@ export {
|
|
|
66
68
|
usePayments,
|
|
67
69
|
useWorkspaceCurrency,
|
|
68
70
|
useSendNotification,
|
|
71
|
+
useRecordMute,
|
|
72
|
+
useRecordMutes,
|
|
69
73
|
useTheme,
|
|
70
74
|
useWidgetStyle,
|
|
71
75
|
useI18n,
|
|
@@ -78,6 +82,9 @@ export {
|
|
|
78
82
|
useFill,
|
|
79
83
|
useNavigation,
|
|
80
84
|
useRouteParams,
|
|
85
|
+
usePageBadge,
|
|
86
|
+
useHardwareBack,
|
|
87
|
+
useFilePreview,
|
|
81
88
|
usePageContext,
|
|
82
89
|
useWidgetRoute,
|
|
83
90
|
useChildRenderer,
|
|
@@ -153,8 +160,14 @@ export {
|
|
|
153
160
|
DateTimePicker,
|
|
154
161
|
FilePicker,
|
|
155
162
|
Gradient,
|
|
163
|
+
Video,
|
|
156
164
|
} from "./primitives.native.js";
|
|
157
165
|
export { lintSource, bannedIdentifiers } from "./linter.js";
|
|
166
|
+
export {
|
|
167
|
+
validateBundledAssets,
|
|
168
|
+
sniffBundledAssetType,
|
|
169
|
+
isValidBundledAssetName,
|
|
170
|
+
} from "./bundled-assets.js";
|
|
158
171
|
// sc-3696: the trailing six are the theme-derivation helpers both hosts resolve
|
|
159
172
|
// `useTheme()` with (the Player through widgetTheme.js, the export through its
|
|
160
173
|
// generated theme module), plus the gradient angle projection `<Gradient>`
|
|
@@ -208,6 +221,7 @@ export { List, ListRow, ListHeader, EmptyState } from "./list.native.js";
|
|
|
208
221
|
// Themed detail parts; the stock Badge/Avatar/Progress/Stat/Divider wrap these.
|
|
209
222
|
export { Badge, Avatar, Progress, Stat, Divider } from "./detail-parts.native.js";
|
|
210
223
|
export { initialsFor } from "./initials.js";
|
|
224
|
+
export { avatarColorFor } from "./avatar-color.js";
|
|
211
225
|
// The one card surface: the theme's `card` scope, a gradient fill, margin.
|
|
212
226
|
export { Card } from "./card.native.js";
|
|
213
227
|
export {
|
package/dist/initials.js
CHANGED
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
// Returns "" when the name yields no letters, so each caller keeps its own
|
|
5
5
|
// fallback (a "?", a person glyph).
|
|
6
6
|
|
|
7
|
-
/** "Ada Lovelace" → "AL", "
|
|
7
|
+
/** "Ada Lovelace" → "AL", "Börje" → "BÖ", " " → "". */
|
|
8
8
|
export function initialsFor(name) {
|
|
9
9
|
const parts =
|
|
10
10
|
typeof name === "string" ? name.trim().split(/\s+/).filter(Boolean) : [];
|
|
11
11
|
if (parts.length === 0) return "";
|
|
12
12
|
// Array.from, not slice: a multi-byte first character must not be halved.
|
|
13
13
|
const first = Array.from(parts[0])[0];
|
|
14
|
-
|
|
14
|
+
// sc-10268: one word still gets two letters, as the Studio's member lists show.
|
|
15
|
+
if (parts.length === 1) return Array.from(parts[0]).slice(0, 2).join("").toUpperCase();
|
|
15
16
|
const last = Array.from(parts[parts.length - 1])[0];
|
|
16
17
|
return (first + last).toUpperCase();
|
|
17
18
|
}
|
package/dist/linter.cjs
CHANGED
|
@@ -1082,6 +1082,33 @@ function _jsxOpenTagEnd(source, from) {
|
|
|
1082
1082
|
return source.length;
|
|
1083
1083
|
}
|
|
1084
1084
|
|
|
1085
|
+
// sc-10721 — a useBundledAsset literal must name a file the package ships.
|
|
1086
|
+
const BUNDLED_ASSET_CALL_RE =
|
|
1087
|
+
/\buseBundledAsset\s*\(\s*(?:"([^"\n]*)"|'([^'\n]*)'|`([^`]*)`)\s*[,)]/g;
|
|
1088
|
+
|
|
1089
|
+
function _bundledAssetRules(source, assetFiles) {
|
|
1090
|
+
if (!Array.isArray(assetFiles)) return [];
|
|
1091
|
+
const findings = [];
|
|
1092
|
+
// Comments blanked, strings kept: the file name IS a string literal.
|
|
1093
|
+
const code = _stripNonCode(source, { keepStrings: true });
|
|
1094
|
+
const sourceLines = source.split(/\r?\n/);
|
|
1095
|
+
for (const match of code.matchAll(BUNDLED_ASSET_CALL_RE)) {
|
|
1096
|
+
const name = match[1] || match[2] || match[3] || "";
|
|
1097
|
+
if (name.includes("${") || assetFiles.includes(name)) continue;
|
|
1098
|
+
const line = code.slice(0, match.index).split(/\r?\n/).length;
|
|
1099
|
+
findings.push({
|
|
1100
|
+
rule: "bundled-asset-missing",
|
|
1101
|
+
severity: "error",
|
|
1102
|
+
label:
|
|
1103
|
+
`useBundledAsset("${name}") names a file that is not in the package's ` +
|
|
1104
|
+
"assets/ directory. Add the file there or correct the name.",
|
|
1105
|
+
line,
|
|
1106
|
+
snippet: (sourceLines[line - 1] || "").trim().slice(0, 200),
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
return findings;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1085
1112
|
// sc-4649 / sc-4686 — see linter.js for the rationale comments. The two
|
|
1086
1113
|
// files must stay in lockstep (a test asserts behaviour-equivalence).
|
|
1087
1114
|
const REQUEST_PAYMENT_CALL = "requestPayment(";
|
|
@@ -2015,6 +2042,7 @@ function lintSource(source, options) {
|
|
|
2015
2042
|
findings.push(..._translationApiRules(source));
|
|
2016
2043
|
findings.push(..._handBuiltPageUrlRules(source));
|
|
2017
2044
|
findings.push(..._lucideIconRules(source));
|
|
2045
|
+
findings.push(..._bundledAssetRules(source, options && options.assetFiles));
|
|
2018
2046
|
findings.push(..._selfContainedReferenceRules(source));
|
|
2019
2047
|
findings.push(..._imagePercentHeightRules(source));
|
|
2020
2048
|
// sc-7274 — soft warning: a percentage flexBasis sizes the main axis, so in a
|
package/dist/linter.js
CHANGED
|
@@ -1081,6 +1081,33 @@ function _jsxOpenTagEnd(source, from) {
|
|
|
1081
1081
|
return source.length;
|
|
1082
1082
|
}
|
|
1083
1083
|
|
|
1084
|
+
// sc-10721 — a useBundledAsset literal must name a file the package ships.
|
|
1085
|
+
const BUNDLED_ASSET_CALL_RE =
|
|
1086
|
+
/\buseBundledAsset\s*\(\s*(?:"([^"\n]*)"|'([^'\n]*)'|`([^`]*)`)\s*[,)]/g;
|
|
1087
|
+
|
|
1088
|
+
function _bundledAssetRules(source, assetFiles) {
|
|
1089
|
+
if (!Array.isArray(assetFiles)) return [];
|
|
1090
|
+
const findings = [];
|
|
1091
|
+
// Comments blanked, strings kept: the file name IS a string literal.
|
|
1092
|
+
const code = _stripNonCode(source, { keepStrings: true });
|
|
1093
|
+
const sourceLines = source.split(/\r?\n/);
|
|
1094
|
+
for (const match of code.matchAll(BUNDLED_ASSET_CALL_RE)) {
|
|
1095
|
+
const name = match[1] || match[2] || match[3] || "";
|
|
1096
|
+
if (name.includes("${") || assetFiles.includes(name)) continue;
|
|
1097
|
+
const line = code.slice(0, match.index).split(/\r?\n/).length;
|
|
1098
|
+
findings.push({
|
|
1099
|
+
rule: "bundled-asset-missing",
|
|
1100
|
+
severity: "error",
|
|
1101
|
+
label:
|
|
1102
|
+
`useBundledAsset("${name}") names a file that is not in the package's ` +
|
|
1103
|
+
"assets/ directory. Add the file there or correct the name.",
|
|
1104
|
+
line,
|
|
1105
|
+
snippet: (sourceLines[line - 1] || "").trim().slice(0, 200),
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
return findings;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1084
1111
|
// sc-4649 / sc-4686 — payment-currency.
|
|
1085
1112
|
//
|
|
1086
1113
|
// A widget must not name a currency AT ALL. Each workspace charges its app users
|
|
@@ -2080,6 +2107,7 @@ export function lintSource(source, options) {
|
|
|
2080
2107
|
findings.push(..._translationApiRules(source));
|
|
2081
2108
|
findings.push(..._handBuiltPageUrlRules(source));
|
|
2082
2109
|
findings.push(..._lucideIconRules(source));
|
|
2110
|
+
findings.push(..._bundledAssetRules(source, options && options.assetFiles));
|
|
2083
2111
|
// sc-2353 / sc-5897 — widget source must be self-contained: a referenced
|
|
2084
2112
|
// `React` global or SDK export with no import throws at render.
|
|
2085
2113
|
findings.push(..._selfContainedReferenceRules(source));
|
package/dist/manifest.cjs
CHANGED
|
@@ -46,7 +46,8 @@ const VALID_PLATFORMS = new Set(["web", "native"]);
|
|
|
46
46
|
// backend/src/core/services/l10n.service.js). The locale pattern mirrors that
|
|
47
47
|
// service's LANG_CODE_RE so a manifest can't declare a locale the dictionary
|
|
48
48
|
// would reject.
|
|
49
|
-
|
|
49
|
+
// One page of the dictionary's widget namespace (l10n MAX_KEYS_PER_PAGE).
|
|
50
|
+
const TRANSLATIONS_MAX_KEYS = 200;
|
|
50
51
|
const TRANSLATION_VALUE_MAX_BYTES = 1024;
|
|
51
52
|
const TRANSLATION_KEY_RE = /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/;
|
|
52
53
|
const TRANSLATION_LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})?$/;
|
|
@@ -328,6 +329,7 @@ function validateManifest(m) {
|
|
|
328
329
|
}
|
|
329
330
|
|
|
330
331
|
module.exports = {
|
|
332
|
+
TRANSLATIONS_MAX_KEYS,
|
|
331
333
|
validateManifest,
|
|
332
334
|
validateManifestTranslations,
|
|
333
335
|
canonicalCategory,
|
package/dist/manifest.js
CHANGED
|
@@ -60,7 +60,8 @@ const ACTION_TIMEOUT_MAX_MS = 5 * 60 * 1000;
|
|
|
60
60
|
// backend/src/core/services/l10n.service.js). The locale pattern mirrors that
|
|
61
61
|
// service's LANG_CODE_RE so a manifest can't declare a locale the dictionary
|
|
62
62
|
// would reject.
|
|
63
|
-
|
|
63
|
+
// One page of the dictionary's widget namespace (l10n MAX_KEYS_PER_PAGE).
|
|
64
|
+
const TRANSLATIONS_MAX_KEYS = 200;
|
|
64
65
|
const TRANSLATION_VALUE_MAX_BYTES = 1024;
|
|
65
66
|
const TRANSLATION_KEY_RE = /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/;
|
|
66
67
|
const TRANSLATION_LOCALE_RE = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})?$/;
|
|
@@ -374,6 +375,7 @@ function validateManifest(m) {
|
|
|
374
375
|
}
|
|
375
376
|
|
|
376
377
|
export {
|
|
378
|
+
TRANSLATIONS_MAX_KEYS,
|
|
377
379
|
validateManifest,
|
|
378
380
|
validateManifestTranslations,
|
|
379
381
|
canonicalCategory,
|
package/dist/overlay-view.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// (`overflow: hidden` cards, scroll containers) clip it and where React Native
|
|
7
7
|
// has no `position: "fixed"` at all. `<Overlay>` fixes that for both hosts at
|
|
8
8
|
// once by rendering into React Native's `Modal`, which leaves the parent's
|
|
9
|
-
// layout on BOTH platforms —
|
|
9
|
+
// layout on BOTH platforms — the web binding portals it to `document.body`,
|
|
10
10
|
// native hands it to the OS — while keeping the children in the widget's own
|
|
11
11
|
// React tree, so their state, hooks and WidgetContext all survive.
|
|
12
12
|
//
|
|
@@ -74,10 +74,7 @@ export function makeOverlay(rn) {
|
|
|
74
74
|
{
|
|
75
75
|
visible: true,
|
|
76
76
|
transparent: true,
|
|
77
|
-
// Not animated
|
|
78
|
-
// and its Escape handler when the entrance animation ENDS, so a fade
|
|
79
|
-
// leaves a window where the keyboard cannot dismiss it — and a dropped
|
|
80
|
-
// animation event would leave it un-dismissable for good.
|
|
77
|
+
// Not animated: a sheet must answer the tap that opened it at once.
|
|
81
78
|
animationType: "none",
|
|
82
79
|
onRequestClose: requestClose,
|
|
83
80
|
},
|
package/dist/overlay.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
// sc-6607 — `<Overlay>` (web). Binds the shared implementation in
|
|
2
|
-
// ./overlay-view.js to react-native-web's primitives.
|
|
2
|
+
// ./overlay-view.js to react-native-web's primitives, with ./web-modal.js in
|
|
3
|
+
// place of react-native-web's `Modal`, which breaks under React StrictMode.
|
|
3
4
|
//
|
|
4
5
|
// The import is `react-native-web` rather than the bare `react-native`
|
|
5
6
|
// specifier for the rolldown optional-peer-dep reason spelled out at the top of
|
|
6
|
-
// ./primitives.js.
|
|
7
|
-
//
|
|
8
|
-
//
|
|
7
|
+
// ./primitives.js. The modal layer portals its subtree into a `document.body`
|
|
8
|
+
// container at `position: fixed`, which is exactly what makes the overlay
|
|
9
|
+
// escape the widget's clipping ancestors.
|
|
9
10
|
|
|
10
11
|
import * as ReactNative from "react-native-web";
|
|
11
12
|
import { makeOverlay } from "./overlay-view.js";
|
|
13
|
+
import { WebModal } from "./web-modal.js";
|
|
12
14
|
|
|
13
|
-
export const Overlay = makeOverlay(
|
|
15
|
+
export const Overlay = makeOverlay({
|
|
16
|
+
Modal: WebModal,
|
|
17
|
+
View: ReactNative.View,
|
|
18
|
+
Pressable: ReactNative.Pressable,
|
|
19
|
+
StyleSheet: ReactNative.StyleSheet,
|
|
20
|
+
});
|
package/dist/primitives.js
CHANGED
|
@@ -80,3 +80,5 @@ export { FilePicker } from "./filepicker.js";
|
|
|
80
80
|
// `linear-gradient` on a View; the native build (./gradient.native.js) delegates
|
|
81
81
|
// to expo-linear-gradient through the same angle projection.
|
|
82
82
|
export { Gradient } from "./gradient.js";
|
|
83
|
+
// sc-10268 — `<Video>` (web): the browser's <video>, inline with controls.
|
|
84
|
+
export { Video } from "./video.js";
|