@foir/sdk 0.28.0 → 0.30.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/CHANGELOG.md +145 -0
- package/dist/ai/index.d.ts +190 -0
- package/dist/ai.js +566 -0
- package/dist/auth/auth.d.ts +29 -6
- package/dist/auth/code-flow.d.ts +97 -0
- package/dist/auth/grant-table.generated.d.ts +1 -1
- package/dist/auth/grants.d.ts +2 -1
- package/dist/auth/hosted.d.ts +14 -6
- package/dist/auth/index.d.ts +3 -1
- package/dist/auth/operations.d.ts +5 -0
- package/dist/auth/session-client.d.ts +1 -1
- package/dist/auth.js +189 -8
- package/dist/client/platform-client.d.ts +1 -1
- package/dist/data/agents.d.ts +104 -0
- package/dist/data/builtins.d.ts +182 -0
- package/dist/data/index.d.ts +5 -3
- package/dist/data/subjects.d.ts +45 -0
- package/dist/data/subscriptions.d.ts +38 -0
- package/dist/data/transport.d.ts +1 -0
- package/dist/data/ws-transport.d.ts +9 -0
- package/dist/data.js +213 -5
- package/dist/media/ProgressiveMedia.d.ts +27 -1
- package/dist/media.js +39 -10
- package/dist/next/session.d.ts +6 -2
- package/dist/next.js +4 -9
- package/dist/placement.js +8 -2
- package/dist/react/FoirFeedback.d.ts +18 -1
- package/dist/react/FoirProvider.d.ts +6 -2
- package/dist/react/SignIn.d.ts +2 -2
- package/dist/react/feedback-notes.d.ts +7 -0
- package/dist/react.js +267 -17
- package/dist/realtime/index.d.ts +6 -0
- package/dist/realtime.js +2 -1
- package/dist/server/webhook-helpers.d.ts +3 -1
- package/dist/server.js +84 -8
- package/package.json +11 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Ref } from 'react';
|
|
1
2
|
import type { ProgressiveImageSource, ProgressiveVideoSource, VariantTierKey } from './media';
|
|
2
3
|
export interface ProgressiveMediaProps {
|
|
3
4
|
image?: ProgressiveImageSource;
|
|
@@ -25,6 +26,31 @@ export interface ProgressiveMediaProps {
|
|
|
25
26
|
muted?: boolean;
|
|
26
27
|
/** Video playback — loop on end. Default true. */
|
|
27
28
|
loop?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Video playback: show the browser's controls. Defaults to `!autoplay`. A
|
|
31
|
+
* video with controls shows from the start, its poster standing until it
|
|
32
|
+
* plays, so the controls can always be reached.
|
|
33
|
+
*/
|
|
34
|
+
controls?: boolean;
|
|
35
|
+
/** Video: how much the browser fetches before it plays (`<video preload>`). */
|
|
36
|
+
preload?: 'none' | 'metadata' | 'auto';
|
|
37
|
+
/**
|
|
38
|
+
* Where the media sits in its box when it is cropped to fit, as CSS
|
|
39
|
+
* `object-position` (`'30% 60%'`), for a focal point. Applies to the image,
|
|
40
|
+
* the video and its thumbnail. Default centre.
|
|
41
|
+
*/
|
|
42
|
+
objectPosition?: string;
|
|
43
|
+
/**
|
|
44
|
+
* The `<video>` element, once it mounts: for a caller's own play and pause
|
|
45
|
+
* controls, and the events that keep them in step.
|
|
46
|
+
*/
|
|
47
|
+
videoRef?: Ref<HTMLVideoElement>;
|
|
48
|
+
/**
|
|
49
|
+
* Called once when a video's source cannot be played. The video is then
|
|
50
|
+
* removed, and its thumbnail, labelled with `alt`, stands in for it over
|
|
51
|
+
* the colour and blurhash layers.
|
|
52
|
+
*/
|
|
53
|
+
onVideoError?: () => void;
|
|
28
54
|
/**
|
|
29
55
|
* When false, skip loading the full media layer (no `<video>`/full `<img>`
|
|
30
56
|
* mount, no HLS attach, no full-image preload). The lower layers — colour,
|
|
@@ -53,4 +79,4 @@ export interface ProgressiveMediaProps {
|
|
|
53
79
|
*/
|
|
54
80
|
variantTiers?: readonly VariantTierKey[];
|
|
55
81
|
}
|
|
56
|
-
export declare function ProgressiveMedia({ image, video, alt, className, fill, fit, placeholder, autoplay, muted, loop, active, sizes, priority, variantTiers, }: ProgressiveMediaProps): import("react/jsx-runtime").JSX.Element | null;
|
|
82
|
+
export declare function ProgressiveMedia({ image, video, alt, className, fill, fit, placeholder, autoplay, muted, loop, controls, preload, objectPosition, videoRef: externalVideoRef, onVideoError, active, sizes, priority, variantTiers, }: ProgressiveMediaProps): import("react/jsx-runtime").JSX.Element | null;
|
package/dist/media.js
CHANGED
|
@@ -213,6 +213,11 @@ function ProgressiveMedia({
|
|
|
213
213
|
autoplay = true,
|
|
214
214
|
muted = true,
|
|
215
215
|
loop = true,
|
|
216
|
+
controls,
|
|
217
|
+
preload,
|
|
218
|
+
objectPosition,
|
|
219
|
+
videoRef: externalVideoRef,
|
|
220
|
+
onVideoError,
|
|
216
221
|
active = true,
|
|
217
222
|
sizes = "100vw",
|
|
218
223
|
priority = false,
|
|
@@ -225,7 +230,13 @@ function ProgressiveMedia({
|
|
|
225
230
|
const source = video ?? image;
|
|
226
231
|
const isVideo = !!video;
|
|
227
232
|
const [wrapperRef, inViewport] = useInViewport("300px", priority);
|
|
228
|
-
const
|
|
233
|
+
const [failedUrl, setFailedUrl] = useState2(void 0);
|
|
234
|
+
const videoFailed = isVideo && failedUrl !== void 0 && failedUrl === video?.url;
|
|
235
|
+
const [videoSize, setVideoSize] = useState2(
|
|
236
|
+
null
|
|
237
|
+
);
|
|
238
|
+
const videoActive = isVideo && active && !videoFailed && (priority || inViewport);
|
|
239
|
+
const showControls = controls ?? !autoplay;
|
|
229
240
|
const imageActive = !isVideo && active;
|
|
230
241
|
const hlsUrl = video?.hlsUrl;
|
|
231
242
|
const useHls = isVideo && !!hlsUrl;
|
|
@@ -282,6 +293,8 @@ function ProgressiveMedia({
|
|
|
282
293
|
cleanupRef.current = null;
|
|
283
294
|
}
|
|
284
295
|
videoRef.current = el;
|
|
296
|
+
if (typeof externalVideoRef === "function") externalVideoRef(el);
|
|
297
|
+
else if (externalVideoRef) externalVideoRef.current = el;
|
|
285
298
|
if (!el) return;
|
|
286
299
|
if (el.readyState >= 3) {
|
|
287
300
|
onFullReady();
|
|
@@ -299,7 +312,7 @@ function ProgressiveMedia({
|
|
|
299
312
|
el.removeEventListener("playing", handler);
|
|
300
313
|
};
|
|
301
314
|
},
|
|
302
|
-
[onFullReady]
|
|
315
|
+
[onFullReady, externalVideoRef]
|
|
303
316
|
);
|
|
304
317
|
if (!source) {
|
|
305
318
|
if (!placeholder) return null;
|
|
@@ -314,8 +327,9 @@ function ProgressiveMedia({
|
|
|
314
327
|
);
|
|
315
328
|
}
|
|
316
329
|
const effective = !isVideo ? effectiveImageDimensions(image) : null;
|
|
317
|
-
const w = effective?.width ?? source.width;
|
|
318
|
-
const h = effective?.height ?? source.height;
|
|
330
|
+
const w = effective?.width ?? source.width ?? videoSize?.w;
|
|
331
|
+
const h = effective?.height ?? source.height ?? videoSize?.h;
|
|
332
|
+
const positioned = objectPosition ? { objectPosition } : void 0;
|
|
319
333
|
const aspect = w && h ? w / h : 16 / 10;
|
|
320
334
|
return /* @__PURE__ */ jsxs(
|
|
321
335
|
"div",
|
|
@@ -342,13 +356,14 @@ function ProgressiveMedia({
|
|
|
342
356
|
"img",
|
|
343
357
|
{
|
|
344
358
|
src: thumbnailUrl,
|
|
345
|
-
alt: "",
|
|
346
|
-
"aria-hidden": true,
|
|
359
|
+
alt: videoFailed ? alt ?? "" : "",
|
|
360
|
+
"aria-hidden": videoFailed ? void 0 : true,
|
|
347
361
|
width: w,
|
|
348
362
|
height: h,
|
|
349
363
|
loading: priority ? "eager" : "lazy",
|
|
350
364
|
fetchPriority: priority ? "high" : void 0,
|
|
351
|
-
className: `absolute inset-0 h-full w-full ${fitClass} transition-opacity duration-400 ease-in-out ${stage === "thumbnail" || stage === "full" ? "opacity-100" : "opacity-0"}
|
|
365
|
+
className: `absolute inset-0 h-full w-full ${fitClass} transition-opacity duration-400 ease-in-out ${stage === "thumbnail" || stage === "full" ? "opacity-100" : "opacity-0"}`,
|
|
366
|
+
style: positioned
|
|
352
367
|
}
|
|
353
368
|
),
|
|
354
369
|
videoActive ? /* @__PURE__ */ jsx2(
|
|
@@ -363,8 +378,21 @@ function ProgressiveMedia({
|
|
|
363
378
|
muted,
|
|
364
379
|
loop,
|
|
365
380
|
playsInline: true,
|
|
366
|
-
controls:
|
|
367
|
-
|
|
381
|
+
controls: showControls,
|
|
382
|
+
preload,
|
|
383
|
+
"aria-label": alt || void 0,
|
|
384
|
+
onLoadedMetadata: (e) => {
|
|
385
|
+
const v = e.currentTarget;
|
|
386
|
+
if (!source.width && v.videoWidth && v.videoHeight) {
|
|
387
|
+
setVideoSize({ w: v.videoWidth, h: v.videoHeight });
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
onError: () => {
|
|
391
|
+
setFailedUrl(video?.url);
|
|
392
|
+
onVideoError?.();
|
|
393
|
+
},
|
|
394
|
+
className: `absolute inset-0 h-full w-full ${fitClass} transition-opacity duration-500 ease-in-out ${stage === "full" || showControls ? "opacity-100" : "opacity-0"}`,
|
|
395
|
+
style: positioned
|
|
368
396
|
}
|
|
369
397
|
) : imageActive ? (
|
|
370
398
|
// <picture>: browser picks AVIF when it can decode it, else WebP. The
|
|
@@ -383,7 +411,8 @@ function ProgressiveMedia({
|
|
|
383
411
|
height: h,
|
|
384
412
|
loading: priority ? "eager" : "lazy",
|
|
385
413
|
fetchPriority: priority ? "high" : void 0,
|
|
386
|
-
className: `absolute inset-0 h-full w-full ${fitClass} transition-opacity duration-500 ease-in-out ${stage === "full" ? "opacity-100" : "opacity-0"}
|
|
414
|
+
className: `absolute inset-0 h-full w-full ${fitClass} transition-opacity duration-500 ease-in-out ${stage === "full" ? "opacity-100" : "opacity-0"}`,
|
|
415
|
+
style: positioned
|
|
387
416
|
}
|
|
388
417
|
)
|
|
389
418
|
] })
|
package/dist/next/session.d.ts
CHANGED
|
@@ -27,8 +27,12 @@ interface PasswordArgs {
|
|
|
27
27
|
}
|
|
28
28
|
/** Password sign-in in a route handler; persists the session to cookies. */
|
|
29
29
|
export declare function foirSignInWithPassword({ env, store, email, password, cookieOptions, }: PasswordArgs): Promise<FoirUser>;
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Start a sign-up in a route handler: the address is mailed a code, and
|
|
32
|
+
* {@link foirSignInWithOtp} with it finishes the account and persists the
|
|
33
|
+
* session to cookies.
|
|
34
|
+
*/
|
|
35
|
+
export declare function foirRegisterWithPassword({ env, email, password, }: Pick<PasswordArgs, 'env' | 'email' | 'password'>): Promise<void>;
|
|
32
36
|
/** Complete an OTP sign-in in a route handler; persists the session to cookies. */
|
|
33
37
|
export declare function foirSignInWithOtp({ env, store, email, otp, cookieOptions, }: {
|
|
34
38
|
env: FoirEnv;
|
package/dist/next.js
CHANGED
|
@@ -1127,8 +1127,8 @@ async function getSessionUser(adapter) {
|
|
|
1127
1127
|
async function signInWithPassword(adapter, env, vars) {
|
|
1128
1128
|
return saveSession(adapter, await login(env, vars));
|
|
1129
1129
|
}
|
|
1130
|
-
async function signUpWithPassword(
|
|
1131
|
-
|
|
1130
|
+
async function signUpWithPassword(env, vars) {
|
|
1131
|
+
await register(env, vars);
|
|
1132
1132
|
}
|
|
1133
1133
|
async function signInWithOTP(adapter, env, vars) {
|
|
1134
1134
|
return saveSession(adapter, await loginWithOTP(env, vars));
|
|
@@ -1158,15 +1158,10 @@ function foirSignInWithPassword({
|
|
|
1158
1158
|
}
|
|
1159
1159
|
function foirRegisterWithPassword({
|
|
1160
1160
|
env,
|
|
1161
|
-
store,
|
|
1162
1161
|
email,
|
|
1163
|
-
password
|
|
1164
|
-
cookieOptions
|
|
1162
|
+
password
|
|
1165
1163
|
}) {
|
|
1166
|
-
return signUpWithPassword(
|
|
1167
|
-
email,
|
|
1168
|
-
password
|
|
1169
|
-
});
|
|
1164
|
+
return signUpWithPassword(env, { email, password });
|
|
1170
1165
|
}
|
|
1171
1166
|
function foirSignInWithOtp({
|
|
1172
1167
|
env,
|
package/dist/placement.js
CHANGED
|
@@ -32,6 +32,12 @@ var GRANT_TABLE = [
|
|
|
32
32
|
["operations:write", "none", [], []],
|
|
33
33
|
["operations:complete", "none", [], []],
|
|
34
34
|
["status:write", "none", [], []],
|
|
35
|
+
["agents:read", "none", [], []],
|
|
36
|
+
["agents:execute", "agent", [], []],
|
|
37
|
+
["agents:write", "none", [], []],
|
|
38
|
+
["agenttools:execute", "agent_tool", [], []],
|
|
39
|
+
["brief:read", "none", [], []],
|
|
40
|
+
["brief:write", "none", [], []],
|
|
35
41
|
["appconfig:read", "none", [], []],
|
|
36
42
|
["schedules:read", "none", [], []],
|
|
37
43
|
["schedules:write", "none", [], []],
|
|
@@ -70,6 +76,7 @@ var GRANT_TABLE = [
|
|
|
70
76
|
["apikeys:write", "none", [], []],
|
|
71
77
|
["organizations:read", "none", [], []],
|
|
72
78
|
["organizations:write", "none", [], []],
|
|
79
|
+
["orgsettings:write", "none", [], []],
|
|
73
80
|
["organizations:create", "none", [], []],
|
|
74
81
|
["organizations:delete", "none", [], []],
|
|
75
82
|
["organizations:reach", "none", [], []],
|
|
@@ -80,7 +87,7 @@ var GRANT_TABLE = [
|
|
|
80
87
|
["projects:read", "none", [], []],
|
|
81
88
|
["projects:write", "none", [], []],
|
|
82
89
|
["project:stand", "none", [], []],
|
|
83
|
-
["project:manage", "none", [], ["project:stand", "records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:setup", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read.all", "notes:write.all", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "
|
|
90
|
+
["project:manage", "none", [], ["project:stand", "records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:setup", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read.all", "notes:write.all", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "agents:read", "agents:execute", "agents:write", "agenttools:execute", "brief:read", "brief:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "authclients:read", "authclients:write", "domains:read", "domains:write", "loginpages:read", "loginpages:write", "secrets:read", "secrets:list", "secrets:put", "secrets:delete", "appsecrets:read", "appsecrets:list", "appsecrets:put", "appsecrets:delete", "usersecrets:read", "usersecrets:list", "usersecrets:put", "usersecrets:delete", "opsecrets:read", "opsecrets:write", "settings:read", "settings:write", "designtokens:read", "designtokens:write", "models:read", "models:write", "configs:read", "configs:write", "signinproviders:read", "signinproviders:write", "apps:read", "apps:write", "events:read", "targeting:debug", "visitors:forward", "apikeys:read", "apikeys:write", "organizations:read", "organizations:create", "organizations:delete", "organizations:write", "orgsettings:write", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write", "audit:read"]],
|
|
84
91
|
["projects:create", "none", [], []],
|
|
85
92
|
["projects:delete", "none", [], []],
|
|
86
93
|
["tenants:read", "none", [], []],
|
|
@@ -111,7 +118,6 @@ var GRANT_TABLE = [
|
|
|
111
118
|
["usersecrets:list", "none", [], []],
|
|
112
119
|
["usersecrets:put", "none", [], []],
|
|
113
120
|
["usersecrets:delete", "none", [], []],
|
|
114
|
-
["auth:login_with_foir", "none", [], []],
|
|
115
121
|
["signinproviders:read", "none", [], []],
|
|
116
122
|
["signinproviders:write", "none", [], []]
|
|
117
123
|
];
|
|
@@ -59,6 +59,23 @@ export interface FoirFeedbackProps {
|
|
|
59
59
|
* with a backdrop, closed by Escape, the backdrop or its Close button.
|
|
60
60
|
*/
|
|
61
61
|
layout?: 'inline' | 'dialog';
|
|
62
|
+
/**
|
|
63
|
+
* Show the button fixed in a corner of the viewport, rather than where the
|
|
64
|
+
* component is placed, with the inline panel opening above it. Default: where
|
|
65
|
+
* it is placed.
|
|
66
|
+
*/
|
|
67
|
+
launcher?: 'bottom-right' | 'bottom-left';
|
|
68
|
+
/**
|
|
69
|
+
* Let the reader point at the part of the page they mean: a field the host
|
|
70
|
+
* marked with `subjects.field(record, key, { label })`. The note is then filed
|
|
71
|
+
* against that record and names the field, so a page showing two records (a
|
|
72
|
+
* menu and an article) files each comment against the right one.
|
|
73
|
+
*
|
|
74
|
+
* Needs `subjects`, and is offered only while the page shows a marked field.
|
|
75
|
+
* A mouse points by clicking; the keyboard by Tab and Enter; Escape cancels.
|
|
76
|
+
* Default false.
|
|
77
|
+
*/
|
|
78
|
+
pointing?: boolean;
|
|
62
79
|
/**
|
|
63
80
|
* The built-in styles' colours, radius and type. See
|
|
64
81
|
* `FoirFeedbackAppearance`: each is also a CSS custom property, so a
|
|
@@ -121,4 +138,4 @@ export interface FoirFeedbackAppearance {
|
|
|
121
138
|
/** `--foir-feedback-font-size`. Default: inherited. */
|
|
122
139
|
fontSize?: string;
|
|
123
140
|
}
|
|
124
|
-
export declare function FoirFeedback({ client, subjects, subject, label, triggerLabel, title, placeholder, defaultOpen, onSignIn, signIn, layout, appearance, onSubmitted, onVisitorSubmitted, onError, className, }: FoirFeedbackProps): import("react/jsx-runtime").JSX.Element;
|
|
141
|
+
export declare function FoirFeedback({ client, subjects, subject, label, triggerLabel, title, placeholder, defaultOpen, onSignIn, signIn, layout, launcher, pointing, appearance, onSubmitted, onVisitorSubmitted, onError, className, }: FoirFeedbackProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -28,10 +28,14 @@ export interface FoirContextValue {
|
|
|
28
28
|
email: string;
|
|
29
29
|
password: string;
|
|
30
30
|
}): Promise<FoirUser>;
|
|
31
|
+
/**
|
|
32
|
+
* Start a sign-up: the address is mailed a code, and `signInWithOtp` with it
|
|
33
|
+
* finishes the account and signs them in.
|
|
34
|
+
*/
|
|
31
35
|
register(vars: {
|
|
32
36
|
email: string;
|
|
33
37
|
password: string;
|
|
34
|
-
}): Promise<
|
|
38
|
+
}): Promise<void>;
|
|
35
39
|
requestOtp(email: string): Promise<boolean>;
|
|
36
40
|
signInWithOtp(vars: {
|
|
37
41
|
email: string;
|
|
@@ -138,7 +142,7 @@ export declare function useFoirAuth(): {
|
|
|
138
142
|
register: (vars: {
|
|
139
143
|
email: string;
|
|
140
144
|
password: string;
|
|
141
|
-
}) => Promise<
|
|
145
|
+
}) => Promise<void>;
|
|
142
146
|
requestOtp: (email: string) => Promise<boolean>;
|
|
143
147
|
signInWithOtp: (vars: {
|
|
144
148
|
email: string;
|
package/dist/react/SignIn.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { FoirUser } from '../auth';
|
|
|
2
2
|
export interface SignInProps {
|
|
3
3
|
/** `password` (default) or `otp` (email one-time code). Ignored when `hostedUrl` is set. */
|
|
4
4
|
method?: 'password' | 'otp';
|
|
5
|
-
/** Render a single button that redirects to
|
|
5
|
+
/** Render a single button that redirects to the hosted sign-in page instead of an embedded form. */
|
|
6
6
|
hostedUrl?: string;
|
|
7
7
|
/** Show OAuth provider buttons below the form. Default true. */
|
|
8
8
|
oauth?: boolean;
|
|
@@ -15,7 +15,7 @@ export interface SignInProps {
|
|
|
15
15
|
/**
|
|
16
16
|
* A drop-in user sign-in. Renders an email/password (or OTP) form and,
|
|
17
17
|
* unless disabled, OAuth provider buttons. Set `hostedUrl` to instead render a
|
|
18
|
-
* button that redirects to
|
|
18
|
+
* button that redirects to the hosted sign-in page. All state is handled through
|
|
19
19
|
* {@link FoirProvider}; on success it calls `onSignIn` and optionally navigates
|
|
20
20
|
* to `afterSignInUrl`.
|
|
21
21
|
*/
|
|
@@ -113,6 +113,8 @@ export interface FoirFeedbackSubmission {
|
|
|
113
113
|
*/
|
|
114
114
|
subject: FoirSubject;
|
|
115
115
|
body: string;
|
|
116
|
+
/** The field of the subject the reader pointed at, when they pointed at one. */
|
|
117
|
+
anchorField?: string;
|
|
116
118
|
/** The still the visitor chose to attach, or null. */
|
|
117
119
|
screenshot: FoirScreenCapture | null;
|
|
118
120
|
/** Injectable for the presigned PUT. Defaults to `globalThis.fetch`. */
|
|
@@ -132,6 +134,11 @@ export interface FoirVisitorSubmission {
|
|
|
132
134
|
/** What the note is about, as for a signed-in user's. */
|
|
133
135
|
subject: FoirSubject;
|
|
134
136
|
body: string;
|
|
137
|
+
/**
|
|
138
|
+
* The field of the subject the visitor pointed at. The data API takes a
|
|
139
|
+
* field the visitor can read and refuses any other.
|
|
140
|
+
*/
|
|
141
|
+
anchorField?: string;
|
|
135
142
|
/**
|
|
136
143
|
* Where the project's team may reply, as the visitor typed it. Sent only
|
|
137
144
|
* when it is not blank, and never verified by anyone.
|