@foir/sdk 0.27.1 → 0.29.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/dist/react.js CHANGED
@@ -1133,8 +1133,8 @@ async function getSessionUser(adapter) {
1133
1133
  async function signInWithPassword(adapter, env, vars) {
1134
1134
  return saveSession(adapter, await login(env, vars));
1135
1135
  }
1136
- async function signUpWithPassword(adapter, env, vars) {
1137
- return saveSession(adapter, await register(env, vars));
1136
+ async function signUpWithPassword(env, vars) {
1137
+ await register(env, vars);
1138
1138
  }
1139
1139
  async function signInWithOTP(adapter, env, vars) {
1140
1140
  return saveSession(adapter, await loginWithOTP(env, vars));
@@ -1280,6 +1280,7 @@ var GRANT_TABLE = [
1280
1280
  ["apikeys:write", "none", [], []],
1281
1281
  ["organizations:read", "none", [], []],
1282
1282
  ["organizations:write", "none", [], []],
1283
+ ["orgsettings:write", "none", [], []],
1283
1284
  ["organizations:create", "none", [], []],
1284
1285
  ["organizations:delete", "none", [], []],
1285
1286
  ["organizations:reach", "none", [], []],
@@ -1290,7 +1291,7 @@ var GRANT_TABLE = [
1290
1291
  ["projects:read", "none", [], []],
1291
1292
  ["projects:write", "none", [], []],
1292
1293
  ["project:stand", "none", [], []],
1293
- ["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", "auth:login_with_foir", "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", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write", "audit:read"]],
1294
+ ["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", "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"]],
1294
1295
  ["projects:create", "none", [], []],
1295
1296
  ["projects:delete", "none", [], []],
1296
1297
  ["tenants:read", "none", [], []],
@@ -1321,7 +1322,6 @@ var GRANT_TABLE = [
1321
1322
  ["usersecrets:list", "none", [], []],
1322
1323
  ["usersecrets:put", "none", [], []],
1323
1324
  ["usersecrets:delete", "none", [], []],
1324
- ["auth:login_with_foir", "none", [], []],
1325
1325
  ["signinproviders:read", "none", [], []],
1326
1326
  ["signinproviders:write", "none", [], []]
1327
1327
  ];
@@ -1680,13 +1680,11 @@ function FoirProvider({
1680
1680
  [apply, assertTokenMode]
1681
1681
  );
1682
1682
  const register2 = useCallback(
1683
- async (vars) => {
1683
+ (vars) => {
1684
1684
  assertTokenMode("register");
1685
- const c = await signUpWithPassword(adapterRef.current, envRef.current, vars);
1686
- apply(c);
1687
- return c;
1685
+ return signUpWithPassword(envRef.current, vars);
1688
1686
  },
1689
- [apply, assertTokenMode]
1687
+ [assertTokenMode]
1690
1688
  );
1691
1689
  const requestOtp = useCallback(
1692
1690
  (email) => {
@@ -2393,16 +2391,29 @@ function screenshotFilename(mimeType) {
2393
2391
 
2394
2392
  // src/react/FoirFeedback.tsx
2395
2393
  import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2394
+ var TOKENS = {
2395
+ colorText: "--foir-feedback-text",
2396
+ colorMuted: "--foir-feedback-muted",
2397
+ colorBackground: "--foir-feedback-background",
2398
+ colorBorder: "--foir-feedback-border",
2399
+ colorPrimary: "--foir-feedback-primary",
2400
+ colorPrimaryText: "--foir-feedback-primary-text",
2401
+ colorDanger: "--foir-feedback-danger",
2402
+ radius: "--foir-feedback-radius",
2403
+ fontFamily: "--foir-feedback-font-family",
2404
+ fontSize: "--foir-feedback-font-size"
2405
+ };
2396
2406
  var COPY = {
2397
2407
  signIn: "Sign in to send feedback.",
2398
2408
  signInAction: "Sign in",
2409
+ closed: "Feedback is not open right now.",
2410
+ close: "Close",
2399
2411
  notEnabled: "Feedback is not available on this account.",
2400
2412
  unavailable: "Feedback is unavailable right now. Please try again later.",
2401
2413
  noSubject: "There is nothing on this page to give feedback on.",
2402
2414
  loading: "One moment\u2026",
2403
2415
  sendFailed: "Your feedback could not be sent. Please try again.",
2404
2416
  sent: "Thanks. Your feedback has been sent.",
2405
- reference: "Reference",
2406
2417
  contactEmail: "Your email, if you would like a reply (optional)",
2407
2418
  send: "Send feedback",
2408
2419
  sending: "Sending\u2026",
@@ -2413,52 +2424,98 @@ var COPY = {
2413
2424
  removeScreenshot: "Remove screenshot",
2414
2425
  about: "About"
2415
2426
  };
2427
+ var border = "1px solid var(--foir-feedback-border, currentColor)";
2428
+ var radius = "var(--foir-feedback-radius, 8px)";
2429
+ var controlRadius = `calc(${radius} * 0.75)`;
2430
+ var background = "var(--foir-feedback-background, transparent)";
2431
+ function wrapperStyle(appearance) {
2432
+ const style = {
2433
+ color: "var(--foir-feedback-text)",
2434
+ fontFamily: "var(--foir-feedback-font-family)",
2435
+ fontSize: "var(--foir-feedback-font-size)"
2436
+ };
2437
+ for (const [name, value] of Object.entries(appearance ?? {})) {
2438
+ const property = TOKENS[name];
2439
+ if (property && typeof value === "string" && value !== "") style[property] = value;
2440
+ }
2441
+ return style;
2442
+ }
2416
2443
  var stackStyle = { display: "flex", flexDirection: "column", gap: 10 };
2417
2444
  var panelStyle = {
2418
2445
  ...stackStyle,
2419
2446
  maxWidth: 340,
2447
+ marginTop: 8,
2420
2448
  padding: 16,
2421
- borderRadius: 8,
2422
- border: "1px solid currentColor",
2449
+ borderRadius: radius,
2450
+ border,
2451
+ background,
2452
+ font: "inherit"
2453
+ };
2454
+ var dialogStyle = {
2455
+ // The browser centres a modal dialog with its margin, and a CSS reset that
2456
+ // zeroes every margin (Tailwind's preflight does) pins it to the corner.
2457
+ margin: "auto",
2458
+ width: "min(420px, calc(100vw - 32px))",
2459
+ padding: 0,
2460
+ borderRadius: radius,
2461
+ border,
2462
+ background: "var(--foir-feedback-background, Canvas)",
2463
+ color: "var(--foir-feedback-text, CanvasText)",
2423
2464
  font: "inherit"
2424
2465
  };
2466
+ var dialogPanelStyle = { ...stackStyle, padding: 20 };
2467
+ var headerStyle = {
2468
+ display: "flex",
2469
+ alignItems: "center",
2470
+ justifyContent: "space-between",
2471
+ gap: 12
2472
+ };
2425
2473
  var buttonStyle = {
2426
2474
  padding: "8px 10px",
2427
- borderRadius: 6,
2428
- border: "1px solid currentColor",
2475
+ borderRadius: controlRadius,
2476
+ border,
2429
2477
  background: "transparent",
2478
+ color: "inherit",
2430
2479
  font: "inherit",
2431
2480
  cursor: "pointer"
2432
2481
  };
2433
- var submitStyle2 = { ...buttonStyle, fontWeight: 600 };
2434
- var textareaStyle = {
2435
- padding: "8px 10px",
2436
- borderRadius: 6,
2437
- border: "1px solid currentColor",
2438
- background: "transparent",
2439
- font: "inherit",
2440
- minHeight: 96,
2441
- resize: "vertical"
2482
+ var closeStyle = { ...buttonStyle, padding: "4px 8px" };
2483
+ var submitStyle2 = {
2484
+ ...buttonStyle,
2485
+ border: "1px solid var(--foir-feedback-primary, var(--foir-feedback-border, currentColor))",
2486
+ background: "var(--foir-feedback-primary, transparent)",
2487
+ color: "var(--foir-feedback-primary-text)",
2488
+ fontWeight: 600
2442
2489
  };
2443
2490
  var inputStyle2 = {
2444
2491
  padding: "8px 10px",
2445
- borderRadius: 6,
2446
- border: "1px solid currentColor",
2447
- background: "transparent",
2492
+ borderRadius: controlRadius,
2493
+ border,
2494
+ background,
2495
+ color: "inherit",
2448
2496
  font: "inherit"
2449
2497
  };
2450
- var headingStyle = { margin: 0, fontSize: 15, fontWeight: 600 };
2451
- var subjectStyle = { margin: 0, fontSize: 13, opacity: 0.75 };
2452
- var noteStyle = { margin: 0, fontSize: 13 };
2453
- var errorStyle2 = { margin: 0, fontSize: 13, color: "#c0392b" };
2498
+ var textareaStyle = { ...inputStyle2, minHeight: 96, resize: "vertical" };
2499
+ var headingStyle = { margin: 0, fontSize: "1.07em", fontWeight: 600 };
2500
+ var subjectStyle = {
2501
+ margin: 0,
2502
+ fontSize: "0.93em",
2503
+ color: "var(--foir-feedback-muted, color-mix(in srgb, currentColor 75%, transparent))"
2504
+ };
2505
+ var noteStyle = { margin: 0, fontSize: "0.93em" };
2506
+ var errorStyle2 = {
2507
+ margin: 0,
2508
+ fontSize: "0.93em",
2509
+ color: "var(--foir-feedback-danger, #c0392b)"
2510
+ };
2454
2511
  var previewStyle = {
2455
2512
  display: "block",
2456
2513
  width: "100%",
2457
2514
  height: 96,
2458
2515
  objectFit: "cover",
2459
2516
  objectPosition: "top",
2460
- borderRadius: 6,
2461
- border: "1px solid currentColor"
2517
+ borderRadius: controlRadius,
2518
+ border
2462
2519
  };
2463
2520
  function FoirFeedback({
2464
2521
  client,
@@ -2470,6 +2527,9 @@ function FoirFeedback({
2470
2527
  placeholder = "What would you like to tell us?",
2471
2528
  defaultOpen = false,
2472
2529
  onSignIn,
2530
+ signIn = true,
2531
+ layout = "inline",
2532
+ appearance,
2473
2533
  onSubmitted,
2474
2534
  onVisitorSubmitted,
2475
2535
  onError,
@@ -2479,7 +2539,6 @@ function FoirFeedback({
2479
2539
  const [probe, setProbe] = useState4({ status: "loading" });
2480
2540
  const [body, setBody] = useState4("");
2481
2541
  const [contactEmail, setContactEmail] = useState4("");
2482
- const [receipt, setReceipt] = useState4(null);
2483
2542
  const [screenshot, setScreenshot] = useState4(null);
2484
2543
  const [capturing, setCapturing] = useState4(false);
2485
2544
  const [busy, setBusy] = useState4(false);
@@ -2508,6 +2567,7 @@ function FoirFeedback({
2508
2567
  if (cancelled) return;
2509
2568
  const fault = classifyNotesFault(err);
2510
2569
  reportFault(fault, err, "the notes surface could not be read");
2570
+ announceClosed(fault, signIn);
2511
2571
  onErrorRef.current?.(err);
2512
2572
  setProbe({ status: "blocked", fault });
2513
2573
  }
@@ -2515,7 +2575,19 @@ function FoirFeedback({
2515
2575
  return () => {
2516
2576
  cancelled = true;
2517
2577
  };
2518
- }, [open, client]);
2578
+ }, [open, client, signIn]);
2579
+ const dialogRef = useRef2(null);
2580
+ useEffect3(() => {
2581
+ const dialog2 = dialogRef.current;
2582
+ if (!dialog2) return;
2583
+ if (open && !dialog2.open) {
2584
+ if (typeof dialog2.showModal === "function") dialog2.showModal();
2585
+ else dialog2.setAttribute("open", "");
2586
+ } else if (!open && dialog2.open) {
2587
+ if (typeof dialog2.close === "function") dialog2.close();
2588
+ else dialog2.removeAttribute("open");
2589
+ }
2590
+ }, [open, layout]);
2519
2591
  const capture = useCallback2(async () => {
2520
2592
  setFailure(null);
2521
2593
  setCapturing(true);
@@ -2555,7 +2627,6 @@ function FoirFeedback({
2555
2627
  });
2556
2628
  setBody("");
2557
2629
  setContactEmail("");
2558
- setReceipt(written);
2559
2630
  setSent(true);
2560
2631
  onVisitorSubmitted?.(written);
2561
2632
  }
@@ -2563,7 +2634,15 @@ function FoirFeedback({
2563
2634
  const fault = classifyNotesFault(err);
2564
2635
  reportFault(fault, err, "sending feedback failed");
2565
2636
  onErrorRef.current?.(err);
2566
- setFailure(failureCopy(fault));
2637
+ if (!probe.signedIn && fault.kind === "other") {
2638
+ const now = await probeRefusal(client);
2639
+ if (now) {
2640
+ announceClosed(now, signIn);
2641
+ setProbe({ status: "blocked", fault: now });
2642
+ return;
2643
+ }
2644
+ }
2645
+ setFailure(failureCopy(fault, signIn));
2567
2646
  } finally {
2568
2647
  setBusy(false);
2569
2648
  }
@@ -2574,157 +2653,214 @@ function FoirFeedback({
2574
2653
  return;
2575
2654
  }
2576
2655
  setSent(false);
2577
- setReceipt(null);
2578
2656
  setFailure(null);
2579
2657
  setOpen(true);
2580
2658
  };
2581
2659
  const subjectName = resolved ? label ?? resolved.label : void 0;
2582
- return /* @__PURE__ */ jsxs4("div", { className, "data-foir-feedback": true, children: [
2583
- /* @__PURE__ */ jsx5(
2584
- "button",
2585
- {
2586
- type: "button",
2587
- "data-foir-feedback-trigger": true,
2588
- "aria-expanded": open,
2589
- onClick: toggle,
2590
- style: styled ? buttonStyle : void 0,
2591
- children: triggerLabel
2592
- }
2593
- ),
2594
- open && /* @__PURE__ */ jsxs4(
2595
- "section",
2596
- {
2597
- "data-foir-feedback-panel": true,
2598
- "aria-label": title,
2599
- style: styled ? panelStyle : void 0,
2600
- children: [
2601
- /* @__PURE__ */ jsx5("h2", { "data-foir-feedback-title": true, style: styled ? headingStyle : void 0, children: title }),
2602
- subjectName && /* @__PURE__ */ jsxs4("p", { "data-foir-feedback-subject": true, style: styled ? subjectStyle : void 0, children: [
2603
- COPY.about,
2604
- ": ",
2605
- subjectName
2606
- ] }),
2607
- probe.status === "loading" && /* @__PURE__ */ jsx5(
2608
- "p",
2660
+ const dialog = layout === "dialog";
2661
+ const heading = /* @__PURE__ */ jsx5("h2", { "data-foir-feedback-title": true, style: styled ? headingStyle : void 0, children: title });
2662
+ const panel = open && /* @__PURE__ */ jsxs4(
2663
+ "section",
2664
+ {
2665
+ "data-foir-feedback-panel": true,
2666
+ "aria-label": title,
2667
+ style: styled ? dialog ? dialogPanelStyle : panelStyle : void 0,
2668
+ children: [
2669
+ dialog ? /* @__PURE__ */ jsxs4("div", { "data-foir-feedback-header": true, style: styled ? headerStyle : void 0, children: [
2670
+ heading,
2671
+ /* @__PURE__ */ jsx5(
2672
+ "button",
2609
2673
  {
2610
- role: "status",
2611
- "data-foir-feedback-status": true,
2612
- style: styled ? noteStyle : void 0,
2613
- children: COPY.loading
2674
+ type: "button",
2675
+ "data-foir-feedback-close": true,
2676
+ onClick: () => setOpen(false),
2677
+ style: styled ? closeStyle : void 0,
2678
+ children: COPY.close
2679
+ }
2680
+ )
2681
+ ] }) : heading,
2682
+ subjectName && /* @__PURE__ */ jsxs4("p", { "data-foir-feedback-subject": true, style: styled ? subjectStyle : void 0, children: [
2683
+ COPY.about,
2684
+ ": ",
2685
+ subjectName
2686
+ ] }),
2687
+ probe.status === "loading" && /* @__PURE__ */ jsx5(
2688
+ "p",
2689
+ {
2690
+ role: "status",
2691
+ "data-foir-feedback-status": true,
2692
+ style: styled ? noteStyle : void 0,
2693
+ children: COPY.loading
2694
+ }
2695
+ ),
2696
+ probe.status === "blocked" && /* @__PURE__ */ jsx5(
2697
+ BlockedNotice,
2698
+ {
2699
+ fault: probe.fault,
2700
+ onSignIn,
2701
+ signIn,
2702
+ styled
2703
+ }
2704
+ ),
2705
+ probe.status === "ready" && !resolved && /* @__PURE__ */ jsx5(
2706
+ "p",
2707
+ {
2708
+ role: "status",
2709
+ "data-foir-feedback-status": true,
2710
+ style: styled ? noteStyle : void 0,
2711
+ children: COPY.noSubject
2712
+ }
2713
+ ),
2714
+ probe.status === "ready" && resolved && /* @__PURE__ */ jsxs4("form", { onSubmit, style: styled ? stackStyle : void 0, children: [
2715
+ /* @__PURE__ */ jsx5(
2716
+ "textarea",
2717
+ {
2718
+ "data-foir-feedback-body": true,
2719
+ "aria-label": placeholder,
2720
+ placeholder,
2721
+ required: true,
2722
+ value: body,
2723
+ disabled: busy,
2724
+ onChange: (event) => {
2725
+ setBody(event.target.value);
2726
+ if (sent) setSent(false);
2727
+ },
2728
+ style: styled ? textareaStyle : void 0
2614
2729
  }
2615
2730
  ),
2616
- probe.status === "blocked" && /* @__PURE__ */ jsx5(BlockedNotice, { fault: probe.fault, onSignIn, styled }),
2617
- probe.status === "ready" && !resolved && /* @__PURE__ */ jsx5(
2618
- "p",
2731
+ !probe.signedIn && /* @__PURE__ */ jsx5(
2732
+ "input",
2619
2733
  {
2620
- role: "status",
2621
- "data-foir-feedback-status": true,
2622
- style: styled ? noteStyle : void 0,
2623
- children: COPY.noSubject
2734
+ type: "email",
2735
+ "data-foir-feedback-contact": true,
2736
+ "aria-label": COPY.contactEmail,
2737
+ placeholder: COPY.contactEmail,
2738
+ autoComplete: "email",
2739
+ value: contactEmail,
2740
+ disabled: busy,
2741
+ onChange: (event) => setContactEmail(event.target.value),
2742
+ style: styled ? inputStyle2 : void 0
2624
2743
  }
2625
2744
  ),
2626
- probe.status === "ready" && resolved && /* @__PURE__ */ jsxs4("form", { onSubmit, style: styled ? stackStyle : void 0, children: [
2745
+ probe.screenshotsEnabled && isFoirScreenCaptureSupported() && /* @__PURE__ */ jsx5(Fragment2, { children: screenshot ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
2627
2746
  /* @__PURE__ */ jsx5(
2628
- "textarea",
2747
+ "img",
2629
2748
  {
2630
- "data-foir-feedback-body": true,
2631
- "aria-label": placeholder,
2632
- placeholder,
2633
- required: true,
2634
- value: body,
2635
- disabled: busy,
2636
- onChange: (event) => {
2637
- setBody(event.target.value);
2638
- if (sent) {
2639
- setSent(false);
2640
- setReceipt(null);
2641
- }
2642
- },
2643
- style: styled ? textareaStyle : void 0
2749
+ "data-foir-feedback-screenshot": true,
2750
+ src: screenshot.dataUrl,
2751
+ alt: COPY.screenshotAlt,
2752
+ style: styled ? previewStyle : void 0
2644
2753
  }
2645
2754
  ),
2646
- !probe.signedIn && /* @__PURE__ */ jsx5(
2647
- "input",
2648
- {
2649
- type: "email",
2650
- "data-foir-feedback-contact": true,
2651
- "aria-label": COPY.contactEmail,
2652
- placeholder: COPY.contactEmail,
2653
- autoComplete: "email",
2654
- value: contactEmail,
2655
- disabled: busy,
2656
- onChange: (event) => setContactEmail(event.target.value),
2657
- style: styled ? inputStyle2 : void 0
2658
- }
2659
- ),
2660
- probe.screenshotsEnabled && isFoirScreenCaptureSupported() && /* @__PURE__ */ jsx5(Fragment2, { children: screenshot ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
2661
- /* @__PURE__ */ jsx5(
2662
- "img",
2663
- {
2664
- "data-foir-feedback-screenshot": true,
2665
- src: screenshot.dataUrl,
2666
- alt: COPY.screenshotAlt,
2667
- style: styled ? previewStyle : void 0
2668
- }
2669
- ),
2670
- /* @__PURE__ */ jsx5(
2671
- "button",
2672
- {
2673
- type: "button",
2674
- "data-foir-feedback-remove": true,
2675
- onClick: () => setScreenshot(null),
2676
- disabled: busy,
2677
- style: styled ? buttonStyle : void 0,
2678
- children: COPY.removeScreenshot
2679
- }
2680
- )
2681
- ] }) : /* @__PURE__ */ jsx5(
2755
+ /* @__PURE__ */ jsx5(
2682
2756
  "button",
2683
2757
  {
2684
2758
  type: "button",
2685
- "data-foir-feedback-capture": true,
2686
- onClick: () => void capture(),
2687
- disabled: busy || capturing,
2759
+ "data-foir-feedback-remove": true,
2760
+ onClick: () => setScreenshot(null),
2761
+ disabled: busy,
2688
2762
  style: styled ? buttonStyle : void 0,
2689
- children: capturing ? COPY.capturing : COPY.capture
2690
- }
2691
- ) }),
2692
- failure && /* @__PURE__ */ jsx5("p", { role: "alert", "data-foir-feedback-error": true, style: styled ? errorStyle2 : void 0, children: failure }),
2693
- sent && !failure && /* @__PURE__ */ jsx5(
2694
- "p",
2695
- {
2696
- role: "status",
2697
- "data-foir-feedback-status": true,
2698
- style: styled ? noteStyle : void 0,
2699
- children: COPY.sent
2700
- }
2701
- ),
2702
- sent && !failure && receipt && /* @__PURE__ */ jsxs4("p", { "data-foir-feedback-receipt": true, style: styled ? subjectStyle : void 0, children: [
2703
- COPY.reference,
2704
- ": ",
2705
- receipt.id
2706
- ] }),
2707
- /* @__PURE__ */ jsx5(
2708
- "button",
2709
- {
2710
- type: "submit",
2711
- "data-foir-feedback-submit": true,
2712
- disabled: busy || body.trim() === "",
2713
- style: styled ? submitStyle2 : void 0,
2714
- children: busy ? COPY.sending : COPY.send
2763
+ children: COPY.removeScreenshot
2715
2764
  }
2716
2765
  )
2717
- ] })
2718
- ]
2719
- }
2720
- )
2721
- ] });
2766
+ ] }) : /* @__PURE__ */ jsx5(
2767
+ "button",
2768
+ {
2769
+ type: "button",
2770
+ "data-foir-feedback-capture": true,
2771
+ onClick: () => void capture(),
2772
+ disabled: busy || capturing,
2773
+ style: styled ? buttonStyle : void 0,
2774
+ children: capturing ? COPY.capturing : COPY.capture
2775
+ }
2776
+ ) }),
2777
+ failure && /* @__PURE__ */ jsx5("p", { role: "alert", "data-foir-feedback-error": true, style: styled ? errorStyle2 : void 0, children: failure }),
2778
+ sent && !failure && /* @__PURE__ */ jsx5(
2779
+ "p",
2780
+ {
2781
+ role: "status",
2782
+ "data-foir-feedback-status": true,
2783
+ style: styled ? noteStyle : void 0,
2784
+ children: COPY.sent
2785
+ }
2786
+ ),
2787
+ /* @__PURE__ */ jsx5(
2788
+ "button",
2789
+ {
2790
+ type: "submit",
2791
+ "data-foir-feedback-submit": true,
2792
+ disabled: busy || body.trim() === "",
2793
+ style: styled ? submitStyle2 : void 0,
2794
+ children: busy ? COPY.sending : COPY.send
2795
+ }
2796
+ )
2797
+ ] })
2798
+ ]
2799
+ }
2800
+ );
2801
+ return /* @__PURE__ */ jsxs4(
2802
+ "div",
2803
+ {
2804
+ className,
2805
+ "data-foir-feedback": true,
2806
+ "data-foir-feedback-layout": layout,
2807
+ style: styled ? wrapperStyle(appearance) : void 0,
2808
+ children: [
2809
+ /* @__PURE__ */ jsx5(
2810
+ "button",
2811
+ {
2812
+ type: "button",
2813
+ "data-foir-feedback-trigger": true,
2814
+ "aria-expanded": open,
2815
+ "aria-haspopup": dialog ? "dialog" : void 0,
2816
+ onClick: toggle,
2817
+ style: styled ? buttonStyle : void 0,
2818
+ children: triggerLabel
2819
+ }
2820
+ ),
2821
+ dialog ? /* @__PURE__ */ jsx5(
2822
+ "dialog",
2823
+ {
2824
+ ref: dialogRef,
2825
+ "data-foir-feedback-dialog": true,
2826
+ "aria-label": title,
2827
+ onClose: () => setOpen(false),
2828
+ onClick: (event) => {
2829
+ if (event.target === event.currentTarget) setOpen(false);
2830
+ },
2831
+ style: styled ? dialogStyle : void 0,
2832
+ children: panel
2833
+ }
2834
+ ) : panel
2835
+ ]
2836
+ }
2837
+ );
2838
+ }
2839
+ async function probeRefusal(client) {
2840
+ try {
2841
+ await readNoteConfig(client);
2842
+ return null;
2843
+ } catch (err) {
2844
+ const fault = classifyNotesFault(err);
2845
+ return fault.kind === "other" ? null : fault;
2846
+ }
2847
+ }
2848
+ function announceClosed(fault, signIn) {
2849
+ if (process.env.NODE_ENV === "production") return;
2850
+ if (fault.kind !== "signIn" || signIn) return;
2851
+ console.info(
2852
+ `[foir] <FoirFeedback>: the project takes no notes from visitors who are not signed in, and signIn={false} says this site has no sign-in, so the widget says feedback is not open. Turn on "Accept anonymous notes" in the project's Notes settings to take them.`
2853
+ );
2722
2854
  }
2723
2855
  function BlockedNotice({
2724
2856
  fault,
2725
2857
  onSignIn,
2858
+ signIn,
2726
2859
  styled
2727
2860
  }) {
2861
+ if (fault.kind === "signIn" && !signIn) {
2862
+ return /* @__PURE__ */ jsx5("p", { role: "status", "data-foir-feedback-status": true, style: styled ? noteStyle : void 0, children: COPY.closed });
2863
+ }
2728
2864
  if (fault.kind === "signIn") {
2729
2865
  return /* @__PURE__ */ jsxs4(Fragment2, { children: [
2730
2866
  /* @__PURE__ */ jsx5("p", { role: "status", "data-foir-feedback-status": true, style: styled ? noteStyle : void 0, children: COPY.signIn }),
@@ -2742,8 +2878,8 @@ function BlockedNotice({
2742
2878
  }
2743
2879
  return /* @__PURE__ */ jsx5("p", { role: "status", "data-foir-feedback-status": true, style: styled ? noteStyle : void 0, children: fault.kind === "scope" ? COPY.notEnabled : COPY.unavailable });
2744
2880
  }
2745
- function failureCopy(fault) {
2746
- if (fault.kind === "signIn") return COPY.signIn;
2881
+ function failureCopy(fault, signIn) {
2882
+ if (fault.kind === "signIn") return signIn ? COPY.signIn : COPY.closed;
2747
2883
  if (fault.kind === "scope") return COPY.notEnabled;
2748
2884
  return COPY.sendFailed;
2749
2885
  }
@@ -71,6 +71,12 @@ export interface CreateCollabSessionOptions {
71
71
  room: RoomAddress | string;
72
72
  /** Async token source. Invoked before the first connect and on each refresh tick. */
73
73
  getToken: () => string | Promise<string>;
74
+ /**
75
+ * The organization the signed-in user stands at in the room, as a request
76
+ * names it. Omit for the project. The platform admits the join only where
77
+ * the user is a member.
78
+ */
79
+ orgId?: string;
74
80
  /** Caller-owned `Y.Doc` carrying the app-defined shared types. */
75
81
  doc: Y.Doc;
76
82
  /** Local presence. Sets awareness under the `user` field (contract shape). Omit for no presence. */
package/dist/realtime.js CHANGED
@@ -39,12 +39,13 @@ function createCollabSession(options) {
39
39
  refreshMs = 4 * 60 * 1e3,
40
40
  connect = true,
41
41
  params: extraParams,
42
+ orgId,
42
43
  WebSocketPolyfill
43
44
  } = options;
44
45
  const room = roomPath(options.room);
45
46
  const provider = new WebsocketProvider(url, room, doc, {
46
47
  connect: false,
47
- params: { ...extraParams },
48
+ params: { ...extraParams, ...orgId ? { org: orgId } : {} },
48
49
  ...WebSocketPolyfill ? { WebSocketPolyfill } : {}
49
50
  });
50
51
  const awareness = provider.awareness;